Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; /** * Verifies that constants are only assigned a value once. * e.g. var XX = 5; * XX = 3; // error! * XX++; // error! * */ class ConstCheck extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR = DiagnosticType.error( "JSC_CONSTANT_REASSIGNED_VALUE_ERROR", "constant {0} assigned a value more than once"); private final AbstractCompiler compiler; private final Set<Scope.Var> initializedConstants; /** * Creates an instance. */ public ConstCheck(AbstractCompiler compiler) { this.compiler = compiler; this.initializedConstants = new HashSet<Scope.Var>(); } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: if (parent != null && parent.isVar() && n.hasChildren()) { String name = n.getString(); Scope.Var var =

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Convention.getObjectLiteralCast(callNode); } @Override public Collection<String> getIndirectlyDeclaredProperties() { return nextConvention.getIndirectlyDeclaredProperties(); } } /** * The default coding convention. * Should be at the bottom of all proxy chains. */ private static class DefaultCodingConvention implements CodingConvention { private static final long serialVersionUID = 1L; @Override public boolean isConstant(String variableName) { return false; } @Override public boolean isConstantKey(String variableName) { return false; } @Override public boolean isValidEnumKey(String key) { return key != null && key.length() > 0; } @Override public boolean isOptionalParameter(Node parameter) { // be as lax as possible, but this must be mutually exclusive from // var_args parameters. return !isVarArgsParameter(parameter); } @Override public boolean isVarArgsParameter(Node parameter) { // be as lax as possible return parameter.getParent().getLastChild() == parameter; } @Override public boolean isExported(String name, boolean local) { return local && name.startsWith("$super"); } @Override public boolean isExported(String name) { return isExported(name, false) || isExported(name, true); } @Override public boolean isPrivate(String name) { return false; } @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { return null; } @Override public boolean isSuperClassReference(String propertyName) { return false; } @Override public String extractClassNameIfProvide(Node node, Node parent) { String message = "only implemented in GoogleCodingConvention"; throw new UnsupportedOperationException(message); } @Override public String extractClassNameIfRequire(Node node, Node parent) { String message = "only implemented in GoogleCodingConvention"; throw new UnsupportedOperationException(message); } @Override public String getExportPropertyFunction() { return null; } @Override public String getExportSymbolFunction() { return null; } @Override public List<String> identifyTypeDeclarationCall(Node n) { return null; }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { // do nothing } @Override public String getAbstractMethodName() { return null; } @Override public String getSingletonGetterClassName(Node callNode) { return null; } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { // do nothing. } @Override public boolean isInlinableFunction(Node n) { Preconditions.checkState(n.isFunction()); return true; } @Override public DelegateRelationship getDelegateRelationship(Node callNode) { return null; } @Override public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate) { // do nothing. } @Override public String getDelegateSuperclassName() { return null; } @Override public void checkForCallingConventionDefiningCalls(Node n, Map<String, String> delegateCallingConventions) { // do nothing. } @Override public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, StaticScope<JSType> scope, List<ObjectType> delegateProxyPrototypes, Map<String, String> delegateCallingConventions) { // do nothing. } @Override public String getGlobalObject() { return "window"; } @Override public boolean isPropertyTestFunction(Node call) { return false; } @Override public boolean isPrototypeAlias(Node getProp) { return false; } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) { return null; } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return Collections.emptySet(); } @Override public Bind describeFunctionBind(Node n) { return describeFunctionBind(n, false); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { if (!n.isCall()) { return null; } Node callTarget = n.getFirstChild(); String name = callTarget.getQualifiedName(); if (name != null) { if (name

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.debugging.sourcemap.FilePosition; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.nio.charset.Charset; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; /** * CodePrinter prints out JS code in either pretty format or compact format. * * @see CodeGenerator */ class CodePrinter { // The number of characters after which we insert a line break in the code static final int DEFAULT_LINE_LENGTH_THRESHOLD = 500; // There are two separate CodeConsumers, one for pretty-printing and // another for compact printing. // There are two implementations because the CompactCodePrinter // potentially has a very different implementation to the pretty // version. private abstract static class MappedCodePrinter extends CodeConsumer { final private Deque<Mapping> mappings; final private List<Mapping> allMappings; final private boolean createSrcMap; final private SourceMap.DetailLevel sourceMapDetailLevel; protected final StringBuilder code = new StringBuilder(1024); protected final int lineLengthThreshold; protected int lineLength = 0; protected int lineIndex = 0; MappedCodePrinter( int lineLengthThreshold, boolean createSrcMap, SourceMap.DetailLevel sourceMapDetailLevel) { Preconditions.checkState(sourceMapDetailLevel != null); this

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.lineLengthThreshold = lineLengthThreshold <= 0 ? Integer.MAX_VALUE : lineLengthThreshold; this.createSrcMap = createSrcMap; this.sourceMapDetailLevel = sourceMapDetailLevel; this.mappings = createSrcMap ? new ArrayDeque<Mapping>() : null; this.allMappings = createSrcMap ? new ArrayList<Mapping>() : null; } /** * Maintains a mapping from a given node to the position * in the source code at which its generated form was * placed. This position is relative only to the current * run of the CodeConsumer and will be normalized * later on by the SourceMap. * * @see SourceMap */ private static class Mapping { Node node; FilePosition start; FilePosition end; } /** * Starts the source mapping for the given * node at the current position. */ @Override void startSourceMapping(Node node) { Preconditions.checkState(sourceMapDetailLevel != null); Preconditions.checkState(node != null); if (createSrcMap && node.getSourceFileName() != null && node.getLineno() > 0 && sourceMapDetailLevel.apply(node)) { int line = getCurrentLineIndex(); int index = getCurrentCharIndex(); Preconditions.checkState(line >= 0); Mapping mapping = new Mapping(); mapping.node = node; mapping.start = new FilePosition(line, index); mappings.push(mapping); allMappings.add(mapping); } } /** * Finishes the source mapping for the given * node at the current position. */ @Override void endSourceMapping(Node node) { if (createSrcMap && !mappings.isEmpty() && mappings.peek().node == node) { Mapping mapping = mappings.pop(); int line = getCurrentLineIndex(); int index = getCurrentCharIndex(); Preconditions.checkState(line >= 0); mapping.end = new FilePosition(line, index); } } /** * Generates the source map from the given code consumer, * appending the information it saved to the SourceMap * object given. */ void generateSourceMap(SourceMap map){ if (createSrcMap) { for (Mapping mapping

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> @Override void maybeLineBreak() { maybeCutLine(); } /** * This may start a new line if the current line is longer than the line * length threshold. */ @Override void maybeCutLine() { if (lineLength > lineLengthThreshold) { startNewLine(); } } @Override void endLine() { startNewLine(); } @Override void appendBlockStart() { append(" {"); indent++; } @Override void appendBlockEnd() { endLine(); indent--; append("}"); } @Override void listSeparator() { add(", "); maybeLineBreak(); } @Override void endFunction(boolean statementContext) { super.endFunction(statementContext); if (statementContext) { startNewLine(); } } @Override void beginCaseBody() { super.beginCaseBody(); indent++; endLine(); } @Override void endCaseBody() { super.endCaseBody(); indent--; endStatement(); } @Override void appendOp(String op, boolean binOp) { if (binOp) { if (getLastChar() != ' ' && op.charAt(0) != ',') { append(" "); } append(op); append(" "); } else { append(op); } } /** * If the body of a for loop or the then clause of an if statement has * a single statement, should it be wrapped in a block? * {@inheritDoc} */ @Override boolean shouldPreserveExtraBlocks() { // When pretty-printing, always place the statement in its own block // so it is printed on a separate line. This allows breakpoints to be // placed on the statement. return true; } /** * @return The TRY node for the specified CATCH node. */ private Node getTryForCatch(Node n) { return n.getParent().getParent(); } /** * @return Whether the a line break should be added after the specified * BLOCK. */ @Override boolean breakAfterBlockFor(Node n, boolean isStatementContext) { Preconditions.checkState(n.isBlock()); Node parent = n.getParent

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> automatically. */ Builder setLineBreak(boolean lineBreak) { this.lineBreak = lineBreak; return this; } /** * Sets whether line breaking is preferred at end of file. This is useful * if JS serving code needs a place to insert code, such as script tags, * without interfering with source maps. * @param lineBreakAtEnd If true, prefer line breaking at end of file. */ Builder setPreferLineBreakAtEndOfFile(boolean lineBreakAtEnd) { this.preferLineBreakAtEndOfFile = lineBreakAtEnd; return this; } /** * Sets whether to output closure-style type annotations. * @param outputTypes If true, outputs closure-style type annotations. */ Builder setOutputTypes(boolean outputTypes) { this.outputTypes = outputTypes; return this; } /** * Sets the line length threshold that will be used to determine * when to break lines, if line breaking is on. * * @param threshold The line length threshold. */ Builder setLineLengthThreshold(int threshold) { this.lineLengthThreshold = threshold; return this; } /** * Sets the source map to which to write the metadata about * the generated source code. * * @param sourceMap The source map. */ Builder setSourceMap(SourceMap sourceMap) { this.sourceMap = sourceMap; return this; } /** * @param level The detail level to use. */ Builder setSourceMapDetailLevel(SourceMap.DetailLevel level) { Preconditions.checkState(level != null); this.sourceMapDetailLevel = level; return this; } /** * Set the charset to use when determining what characters need to be * escaped in the output. */ Builder setOutputCharset(Charset outCharset) { this.outputCharset = outCharset; return this; } /** * Set whether the output should be tags as ECMASCRIPT 5 Strict. */ Builder setTagAsStrict(boolean tagAsStrict) { this.tagAsStrict = tagAsStrict; return this; } /** * Generates the source code and returns it. */ String build() { if (root == null

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) { throw new IllegalStateException( "Cannot build without root node being specified"); } Format outputFormat = outputTypes ? Format.TYPED : prettyPrint ? Format.PRETTY : Format.COMPACT; return toSource(root, outputFormat, lineBreak, preferLineBreakAtEndOfFile, lineLengthThreshold, sourceMap, sourceMapDetailLevel, outputCharset, tagAsStrict); } } enum Format { COMPACT, PRETTY, TYPED } /** * Converts a tree to JS code */ private static String toSource(Node root, Format outputFormat, boolean lineBreak, boolean preferEndOfFileBreak, int lineLengthThreshold, SourceMap sourceMap, SourceMap.DetailLevel sourceMapDetailLevel, Charset outputCharset, boolean tagAsStrict) { Preconditions.checkState(sourceMapDetailLevel != null); boolean createSourceMap = (sourceMap != null); MappedCodePrinter mcp = outputFormat == Format.COMPACT ? new CompactCodePrinter( lineBreak, preferEndOfFileBreak, lineLengthThreshold, createSourceMap, sourceMapDetailLevel) : new PrettyCodePrinter( lineLengthThreshold, createSourceMap, sourceMapDetailLevel); CodeGenerator cg = outputFormat == Format.TYPED ? new TypedCodeGenerator(mcp, outputCharset) : new CodeGenerator(mcp, outputCharset); if (tagAsStrict) { cg.tagAsStrict(); } cg.add(root); mcp.endFile(); String code = mcp.getCode(); if (createSourceMap) { mcp.generateSourceMap(sourceMap); } return code; } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> the $ must be * upper case. * $A Constant - doesn't have to be anything in front of the $ * </pre> */ @Override public boolean isConstant(String name) { if (name.length() <= 1) { return false; } // In compiled code, '$' is often a namespace delimiter. To allow inlining // of namespaced constants, we strip off any namespaces here. int pos = name.lastIndexOf('$'); if (pos >= 0) { name = name.substring(pos + 1); if (name.length() == 0) { return false; } } return isConstantKey(name); } @Override public boolean isConstantKey(String name) { if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { return false; } // hack way of checking that there aren't any lower-case letters return name.toUpperCase().equals(name); } /** * {@inheritDoc} * * <p>This enforces Google's convention about enum key names. They must match * the regular expression {@code [A-Z0-9][A-Z0-9_]*}. * * <p>Examples: * <ul> * <li>A</li> * <li>213</li> * <li>FOO_BAR</li> * </ul> */ @Override public boolean isValidEnumKey(String key) { return ENUM_KEY_PATTERN.matcher(key).matches(); } /** * {@inheritDoc} * * <p>In Google code, parameter names beginning with {@code opt_} are * treated as optional arguments. */ @Override public boolean isOptionalParameter(Node parameter) { return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX); } @Override public boolean isVarArgsParameter(Node parameter) { return VAR_ARGS_NAME.equals(parameter.getString()); } /** * {@inheritDoc} * * <p>In Google code, any global name starting with an underscore is * considered exported. */ @Override public boolean isExported(String name, boolean local) {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> return super.isExported(name, local) || (!local && name.startsWith("_")); } /** * {@inheritDoc} * * <p>In Google code, private names end with an underscore, and exported * names are never considered private (see {@link #isExported}). */ @Override public boolean isPrivate(String name) { return name.endsWith("_") && !isExported(name); } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS><Var> getReferences(Var var) { return ImmutableList.of(var); } @Override public Scope getScope(Var var) { return var.scope; } @Override public Iterable<Var> getAllSymbols() { List<Var> vars = Lists.newArrayList(); for (Scope s : scopes.values()) { Iterables.addAll(vars, s.getAllSymbols()); } return vars; } @Override public Scope createScope(Node n, Scope parent) { Scope scope = scopes.get(n); if (scope == null) { scope = delegate.createScope(n, parent); scopes.put(n, scope); } else { Preconditions.checkState(parent == scope.getParent()); } return scope; } Collection<Scope> getAllMemoizedScopes() { return Collections.unmodifiableCollection(scopes.values()); } Scope getScopeIfMemoized(Node n) { return scopes.get(n); } /** * Removes all scopes with root nodes from a given script file. * * @param scriptName the name of the script file to remove nodes for. */ void removeScopesForScript(String scriptName) { for (Node scopeRoot : ImmutableSet.copyOf(scopes.keySet())) { if (scriptName.equals(scopeRoot.getSourceFileName())) { scopes.remove(scopeRoot); } } } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>()) { if (isCurrentLoopPopulated) { passes.add(currentLoop); currentLoop = new LoopInternal(); isCurrentLoopPopulated = false; } addOneTimePass(factory); } else { currentLoop.addLoopedPass(factory); isCurrentLoopPopulated = true; } } if (isCurrentLoopPopulated) { passes.add(currentLoop); } } /** * Add the pass generated by the given factory to the compile sequence. * This pass will be run once. */ void addOneTimePass(PassFactory factory) { passes.add(new PassFactoryDelegate(compiler, factory)); } /** * Add a loop to the compile sequence. This loop will continue running * until the AST stops changing. * @return The loop structure. Pass suppliers should be added to the loop. */ Loop addFixedPointLoop() { Loop loop = new LoopInternal(); passes.add(loop); return loop; } /** * Adds a sanity checker to be run after every pass. Intended for development. */ void setSanityCheck(PassFactory sanityCheck) { this.sanityCheck = sanityCheck; } /** * Run all the passes in the optimizer. */ @Override public void process(Node externs, Node root) { double progress = 0.0; double progressStep = 0.0; if (progressRange != null) { progressStep = (progressRange.maxValue - progressRange.initialValue) / passes.size(); progress = progressRange.initialValue; } for (CompilerPass pass : passes) { pass.process(externs, root); if (progressRange != null) { progress += progressStep; compiler.setProgress(progress); } if (hasHaltingErrors()) { return; } } } /** * Marks the beginning of a pass. */ private void startPass(String passName) { Preconditions.checkState(currentTracer == null && currentPassName == null); currentPassName = passName; currentTracer = newTracer(passName); } /** * Marks the end of a pass. */ private void end

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Pass(Node externs, Node root) { Preconditions.checkState(currentTracer != null && currentPassName != null); String passToCheck = currentPassName; try { stopTracer(currentTracer, currentPassName); currentPassName = null; currentTracer = null; maybeSanityCheck(externs, root); } catch (Exception e) { // TODO(johnlenz): Remove this once the normalization checks report // errors instead of exceptions. throw new RuntimeException("Sanity check failed for " + passToCheck, e); } } /** * Runs the sanity check if it is available. */ void maybeSanityCheck(Node externs, Node root) { if (sanityCheck != null) { sanityCheck.create(compiler).process(externs, root); } } private boolean hasHaltingErrors() { return compiler.hasHaltingErrors(); } /** * Returns a new tracer for the given pass name. */ private Tracer newTracer(String passName) { String comment = passName + (recentChange.hasCodeChanged() ? " on recently changed AST" : ""); if (tracker != null) { tracker.recordPassStart(passName); } return new Tracer("JSCompiler", comment); } private void stopTracer(Tracer t, String passName) { long result = t.stop(); if (tracker != null) { tracker.recordPassStop(passName, result); } } /** * A single compiler pass. */ private abstract class NamedPass implements CompilerPass { private final String name; NamedPass(String name) { this.name = name; } @Override public void process(Node externs, Node root) { logger.fine(name); startPass(name); processInternal(externs, root); endPass(externs, root); } abstract void processInternal(Node externs, Node root); } /** * Delegates to a PassFactory for processing. */ private class PassFactoryDelegate extends NamedPass { private final AbstractCompiler myCompiler; private final PassFactory factory; private PassFactoryDelegate( AbstractCompiler myCompiler, PassFactory factory) { super(factory.getName());

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> this.myCompiler = myCompiler; this.factory = factory; } @Override void processInternal(Node externs, Node root) { factory.create(myCompiler).process(externs, root); } } /** * Runs a set of compiler passes until they reach a fixed point. */ static abstract class Loop implements CompilerPass { abstract void addLoopedPass(PassFactory factory); } /** * Runs a set of compiler passes until they reach a fixed point. * * Notice that this is a non-static class, because it includes the closure * of PhaseOptimizer. */ private class LoopInternal extends Loop { private final List<NamedPass> myPasses = Lists.newArrayList(); private final Set<String> myNames = Sets.newHashSet(); @Override void addLoopedPass(PassFactory factory) { String name = factory.getName(); Preconditions.checkArgument(!myNames.contains(name), "Already a pass with name '%s' in this loop", name); myNames.add(factory.getName()); myPasses.add(new PassFactoryDelegate(compiler, factory)); } /** * Gets the pass names, in order. */ private List<String> getPassOrder() { List<String> order = Lists.newArrayList(); for (NamedPass pass : myPasses) { order.add(pass.name); } return order; } @Override public void process(Node externs, Node root) { Preconditions.checkState(!loopMutex, "Nested loops are forbidden"); loopMutex = true; if (randomizeLoops) { randomizePasses(); } else { optimizePasses(); } try { // TODO(nicksantos): Use a smarter algorithm that dynamically adjusts // the order that passes are run in. int count = 0; out: do { if (count++ > MAX_LOOPS) { compiler.throwInternalError(OPTIMIZE_LOOP_ERROR, null); } recentChange.reset(); // reset before this round of optimizations for (CompilerPass pass : myPasses) { pass.process(externs, root); if (hasHaltingErrors()) { break out; }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> true; } @Override public Collection<GraphNode<N, E>> getNodes() { return Collections.<GraphNode<N, E>>unmodifiableCollection(nodes.values()); } @Override public List<GraphNode<N, E>> getNeighborNodes(N value) { DiGraphNode<N, E> node = getDirectedGraphNode(value); return getNeighborNodes(node); } public List<GraphNode<N, E>> getNeighborNodes(DiGraphNode<N, E> node) { List<GraphNode<N, E>> result = Lists.newArrayList(); for (Iterator<GraphNode<N, E>> i = ((LinkedDirectedGraphNode<N, E>) node).neighborIterator();i.hasNext();) { result.add(i.next()); } return result; } @Override public Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value) { LinkedDirectedGraphNode<N, E> node = nodes.get(value); Preconditions.checkNotNull(node); return node.neighborIterator(); } @Override public List<GraphEdge<N, E>> getEdges() { List<GraphEdge<N, E>> result = Lists.newArrayList(); for (DiGraphNode<N, E> node : nodes.values()) { for (DiGraphEdge<N, E> edge : node.getOutEdges()) { result.add(edge); } } return Collections.unmodifiableList(result); } @Override public int getNodeDegree(N value) { DiGraphNode<N, E> node = getNodeOrFail(value); return node.getInEdges().size() + node.getOutEdges().size(); } /** * A directed graph node that stores outgoing edges and incoming edges as an * list within the node itself. */ static class LinkedDirectedGraphNode<N, E> implements DiGraphNode<N, E>, GraphvizNode { List<DiGraphEdge<N, E>> inEdgeList = Lists.newArrayList(); List<DiGraphEdge<N, E>> outEdgeList = Lists.newArrayList(); protected final N value; /** * Constructor * * @param nodeValue Node's value. */

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Prepare the AST before we do any checks or optimizations on it. * * This pass must run. It should bring the AST into a consistent state, * and add annotations where necessary. It should not make any transformations * on the tree that would lose source information, since we need that source * information for checks. * * @author johnlenz@google.com (John Lenz) */ class PrepareAst implements CompilerPass { private final AbstractCompiler compiler; private final boolean checkOnly; PrepareAst(AbstractCompiler compiler) { this(compiler, false); } PrepareAst(AbstractCompiler compiler, boolean checkOnly) { this.compiler = compiler; this.checkOnly = checkOnly; } private void reportChange() { if (checkOnly) { Preconditions.checkState(false, "normalizeNodeType constraints violated"); } } @Override public void process(Node externs, Node root) { if (checkOnly) { normalizeNodeTypes(root); } else { // Don't perform "PrepareAnnotations" when doing checks as // they currently aren't valid during sanity checks. In particular, // they DIRECT_EVAL shouldn't be

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> applied after inlining has been // performed. if (externs != null) { NodeTraversal.traverse( compiler, externs, new PrepareAnnotations(compiler)); } if (root != null) { NodeTraversal.traverse( compiler, root, new PrepareAnnotations(compiler)); } } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && !n.isLabel() && !n.isSwitch()) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n,c) && !c.isBlock()) { Node newBlock = IR.block().srcref(n); n.replaceChild(c, newBlock); if (!c.isEmpty()) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations implements NodeTraversal.Callback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isObjectLit()) { normalizeObjectLiteralAnnotations(n); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.getType()) { case Token.CALL: annotateCalls(n); break; case Token.FUNCTION: annotateFunctions(n, parent); annotateDispatchers(n, parent); break; } } private void normalizeObjectLiteralAnnotations(Node objlit) { Preconditions.checkState(objlit.isObjectLit()); for (Node key = objlit.getFirstChild(); key != null; key = key.getNext()) { Node value = key.getFirstChild(); normalizeObjectLiteralKeyAnnotations(objlit, key, value); } } /** * There are two types of calls we are interested in calls without explicit * "this" values (what we are call "free" calls) and direct call to eval. */ private void annotateCalls(Node n) { Preconditions.checkState(n.isCall()); // Keep track of of the "this" context of a call. A call without an // explicit "this" is a free call. Node first = n.getFirstChild(); if (!NodeUtil.isGet(first)) { n.putBooleanProp(Node.FREE_CALL, true); } // Keep track of the context in which eval is called. It is important // to distinguish between "(0, eval)()" and "eval()". if (first.isName() && "eval".equals(first.getString())) { first.putBooleanProp(Node.DIRECT_EVAL, true); } } /** * Translate dispatcher info into the property expected node. */ private void annotateDispatchers(Node n, Node parent) { Preconditions.checkState(n.isFunction()); if (parent.getJSDocInfo() != null && parent.getJSDocInfo().isJavaDispatch()) { if (parent.isAssign()) { Preconditions.checkState(parent.getLastChild() == n); n.putBooleanProp(Node.IS_DISPATCHER, true); } } } /** * In the AST that Rhino gives us, it needs to make a distinction * between JsDoc on the object literal node and JsDoc on the object literal * value. For example, * <pre> * var x = { * / JSDOC / * a: 'b

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ private void normalizeObjectLiteralKeyAnnotations( Node objlit, Node key, Node value) { Preconditions.checkState(objlit.isObjectLit()); if (key.getJSDocInfo() != null && value.isFunction()) { value.setJSDocInfo(key.getJSDocInfo()); } } /** * Annotate optional and var_arg function parameters. */ private void annotateFunctions(Node n, Node parent) { JSDocInfo fnInfo = NodeUtil.getFunctionJSDocInfo(n); // Compute which function parameters are optional and // which are var_args. Node args = n.getFirstChild().getNext(); for (Node arg = args.getFirstChild(); arg != null; arg = arg.getNext()) { String argName = arg.getString(); JSTypeExpression typeExpr = fnInfo == null ? null : fnInfo.getParameterType(argName); if (convention.isOptionalParameter(arg) || typeExpr != null && typeExpr.isOptionalArg()) { arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true); } if (convention.isVarArgsParameter(arg) || typeExpr != null && typeExpr.isVarArgs()) { arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true); } } } } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> do this inference globally. if (n.isGetProp() && !t.inGlobalScope() && type.isNullType()) { return true; } mismatch(t, n, msg, type, expectedType); return false; } return true; } private boolean containsForwardDeclaredUnresolvedName(JSType type) { if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { if (containsForwardDeclaredUnresolvedName(alt)) { return true; } } } return type.isNoResolvedType(); } /** * Expect that the type of a switch condition matches the type of its * case condition. */ void expectSwitchMatchesCase(NodeTraversal t, Node n, JSType switchType, JSType caseType) { // ECMA-262, page 68, step 3 of evaluation of CaseBlock, // but allowing extra autoboxing. // TODO(user): remove extra conditions when type annotations // in the code base have adapted to the change in the compiler. if (!switchType.canTestForShallowEqualityWith(caseType) && (caseType.autoboxesTo() == null || !caseType.autoboxesTo().isSubtype(switchType))) { mismatch(t, n.getFirstChild(), "case expression doesn't match switch", caseType, switchType); } } /** * Expect that the first type can be addressed with GETELEM syntax, * and that the second type is the right type for an index into the * first type. * * @param t The node traversal. * @param n The GETELEM node to issue warnings on. * @param objType The type of the left side of the GETELEM. * @param indexType The type inside the brackets of the GETELEM. */ void expectIndexMatch(NodeTraversal t, Node n, JSType objType, JSType indexType) { Preconditions.checkState(n.isGetElem()); Node indexNode = n.getLastChild(); if (objType.isUnknownType()) { expectStringOrNumber(t, indexNode, indexType, "property access"); } else { ObjectType dereferenced =

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitialScope and is a // native type. We should redeclare it at the new input site. if (var.input == null) { Scope s = var.getScope(); s.undeclare(var); newVar = s.declare(variableName, n, varType, input, false); n.setJSType(varType); if (parent.isVar()) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.isFunction()); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().isExprResult()) || !newType.equals(varType)) { report(JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } return newVar; } /** * Expect that all properties on interfaces that this type implements are * implemented and correctly typed. */ void expectAllInterfaceProperties(NodeTraversal t, Node n, FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.parsing; import com.google.common.base.Preconditions; import com.google.javascript.rhino.head.ScriptRuntime; /** * This class implements the scanner for JsDoc strings. * * It is heavily based on Rhino's TokenStream. * */ class JsDocTokenStream { /* * For chars - because we need something out-of-range * to check. (And checking EOF by exception is annoying.) * Note distinction from EOF token type! */ private final static int EOF_CHAR = -1; JsDocTokenStream(String sourceString) { this(sourceString, 0); } JsDocTokenStream(String sourceString, int lineno) { this(sourceString, lineno, 0); } JsDocTokenStream(String sourceString, int lineno, int initCharno) { Preconditions.checkNotNull(sourceString); this.lineno = lineno; this.sourceString = sourceString; this.sourceEnd = sourceString.length(); this.sourceCursor = this.cursor = 0; this.initLineno = lineno; this.initCharno = initCharno; } /** * Tokenizes JSDoc comments. */ @SuppressWarnings("fallthrough") final JsDocToken getJsDocToken() { int c; stringBufferTop = 0; for (;;) { // eat white spaces for (;;) { charno = -1; c = getChar(); if (c == EOF_CHAR) { return JsDocToken

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.graph.LatticeElement; import java.util.List; /** * Defines a way join a list of LatticeElements. */ interface JoinOp<L extends LatticeElement> extends Function<List<L>, L> { /** * An implementation of {@code JoinOp} that makes it easy to join to * lattice elements at a time. */ static abstract class BinaryJoinOp<L extends LatticeElement> implements JoinOp<L> { @Override public final L apply(List<L> values) { Preconditions.checkArgument(!values.isEmpty()); int size = values.size(); if (size == 1) { return values.get(0); } else if (size == 2) { return apply(values.get(0), values.get(1)); } else { int mid = computeMidPoint(size); return apply( apply(values.subList(0, mid)), apply(values.subList(mid, size))); } } /** * Creates a new lattice that will be the join of two input lattices. * * @return The join of {@code latticeA} and {@code latticeB}. */ abstract L apply(L latticeA, L latticeB); /** * Finds the midpoint of a list. The function will favor two lists of * even length instead of two lists of the same odd length.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName)); } else if (processDefineAssignment(t, fullName, val, valParent)) { // remove the assignment so that the variable is still declared, // but no longer assigned to a value, e.g., // DEF_FOO = 5; // becomes "5;" // We can't remove the ASSIGN/VAR when we're still visiting its // children, so we'll have to come back later to remove it. refInfo.name.removeRef(ref); lvalueToRemoveLater = valParent; } break; default: if (t.inGlobalScope()) { // Treat this as a reference to a define in the global scope. // After this point, the define must not be reassigned, // or it's an error. DefineInfo info = assignableDefines.get(fullName); if (info != null) { setDefineInfoNotAssignable(info, t); assignableDefines.remove(fullName); } } break; } } if (!t.inGlobalScope() && n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) { // warn about @define annotations in local scopes compiler.report( t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, "")); } if (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.isAssign()) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.isName()); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.isCall()) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which // are

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> = getNativeType(JSTypeNative.UNKNOWN_TYPE); name.setJSType(type); redeclareSimpleVar(scope, name, type); return scope; } private FlowScope traverseAssign(Node n, FlowScope scope) { Node left = n.getFirstChild(); Node right = n.getLastChild(); scope = traverseChildren(n, scope); JSType leftType = left.getJSType(); JSType rightType = getJSType(right); n.setJSType(rightType); updateScopeForTypeChange(scope, left, leftType, rightType); return scope; } /** * Updates the scope according to the result of a type change, like * an assignment or a type cast. */ private void updateScopeForTypeChange( FlowScope scope, Node left, JSType leftType, JSType resultType) { Preconditions.checkNotNull(resultType); switch (left.getType()) { case Token.NAME: String varName = left.getString(); Var var = syntacticScope.getVar(varName); // When looking at VAR initializers for declared VARs, we trust // the declared type over the type it's being initialized to. // This has two purposes: // 1) We avoid re-declaring declared variables so that built-in // types defined in externs are not redeclared. // 2) When there's a lexical closure like // /** @type {?string} */ var x = null; // function f() { x = 'xyz'; } // the inference will ignore the lexical closure, // which is just wrong. This bug needs to be fixed eventually. boolean isVarDeclaration = left.hasChildren(); if (!isVarDeclaration || var == null || var.isTypeInferred()) { redeclareSimpleVar(scope, left, resultType); } left.setJSType(isVarDeclaration || leftType == null ? resultType : null); if (var != null && var.isTypeInferred()) { JSType oldType = var.getType(); var.setType(oldType == null ? resultType : oldType.getLeastSupertype(resultType)); } break; case Token.GETPROP: String qualifiedName = left.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> we have it. // 1) The var is escaped in a weird way, e.g., // function f() { var x = 3; function g() { x = null } (x); } boolean isInferred = var.isTypeInferred(); boolean unflowable = isInferred && isUnflowable(syntacticScope.getVar(varName)); // 2) We're reading type information from another scope for an // inferred variable. // var t = null; function f() { (t); } boolean nonLocalInferredSlot = isInferred && syntacticScope.getParent() != null && var == syntacticScope.getParent().getSlot(varName); if (!unflowable && !nonLocalInferredSlot) { type = var.getType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); } } } } n.setJSType(type); return scope; } /** Traverse each element of the array. */ private FlowScope traverseArrayLiteral(Node n, FlowScope scope) { scope = traverseChildren(n, scope); n.setJSType(getNativeType(ARRAY_TYPE)); return scope; } private FlowScope traverseObjectLiteral(Node n, FlowScope scope) { JSType type = n.getJSType(); Preconditions.checkNotNull(type); for (Node name = n.getFirstChild(); name != null; name = name.getNext()) { scope = traverse(name.getFirstChild(), scope); } // Object literals can be reflected on other types, or changed with // type casts. // See CodingConvention#getObjectLiteralCase and goog.object.reflect. // Ignore these types of literals. // TODO(nicksantos): There should be an "anonymous object" type that // we can check for here. ObjectType objectType = ObjectType.cast(type); if (objectType == null) { return scope; } boolean hasLendsName = n.getJSDocInfo() != null && n.getJSDocInfo().getLendsName() != null; if (objectType.hasReferenceName() && !hasLendsName) { return scope; } String qObjName = NodeUtil.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>} resolvedTemplateType( resolvedTypes, paramType.toMaybeTemplateType(), argType); } else if (paramType.isUnionType()) { // @param {Array.<T>|NodeList|Arguments|{length:number}} UnionType unionType = paramType.toMaybeUnionType(); for (JSType alernative : unionType.getAlternates()) { maybeResolveTemplatedType(alernative, argType, resolvedTypes); } } else if (paramType.isFunctionType()) { FunctionType paramFunctionType = paramType.toMaybeFunctionType(); FunctionType argFunctionType = argType .restrictByNotNullOrUndefined() .collapseUnion() .toMaybeFunctionType(); if (argFunctionType != null && argFunctionType.isSubtype(paramType)) { // infer from return type of the function type maybeResolveTemplatedType( paramFunctionType.getReturnType(), argFunctionType.getReturnType(), resolvedTypes); // infer from parameter types of the function type maybeResolveTemplateTypeFromNodes( paramFunctionType.getParameters(), argFunctionType.getParameters(), resolvedTypes); } } else if (paramType.isParameterizedType()) { ParameterizedType paramObjectType = paramType.toMaybeParameterizedType(); JSType typeParameter = paramObjectType.getParameterType(); Preconditions.checkNotNull(typeParameter); if (typeParameter != null) { // @param {Array.<T>} ObjectType argObjectType = argType .restrictByNotNullOrUndefined() .collapseUnion() .toMaybeParameterizedType(); if (argObjectType != null && argObjectType.isSubtype(paramType)) { JSType argTypeParameter = argObjectType.getParameterType(); Preconditions.checkNotNull(argTypeParameter); maybeResolveTemplatedType( typeParameter, argTypeParameter, resolvedTypes); } } } } private void maybeResolveTemplateTypeFromNodes( Iterable<Node> declParams, Iterable<Node> callParams, Map<TemplateType, JSType> resolvedTypes) { maybeResolveTemplateTypeFromNodes( declParams.iterator(), callParams.iterator(), resolvedTypes); } private void maybeResolveTemplateTypeFromNodes( Iterator<Node> declParams, Iterator<Node> callParams, Map<TemplateType, JSType> resolvedTypes) { while (declParams.hasNext() && callParams

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.hasNext()) { maybeResolveTemplatedType( getJSType(declParams.next()), getJSType(callParams.next()), resolvedTypes); } } private void resolvedTemplateType( Map<TemplateType, JSType> map, TemplateType template, JSType resolved) { JSType previous = map.get(template); if (!resolved.isUnknownType()) { if (previous == null) { map.put(template, resolved); } else { JSType join = previous.getLeastSupertype(resolved); map.put(template, join); } } } private static class TemplateTypeReplacer extends ModificationVisitor { private final Map<TemplateType, JSType> replacements; private final JSTypeRegistry registry; TemplateTypeReplacer( JSTypeRegistry registry, Map<TemplateType, JSType> replacements) { super(registry); this.registry = registry; this.replacements = replacements; } @Override public JSType caseTemplateType(TemplateType type) { JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { if (fnType.getTemplateTypeNames().isEmpty()) { return false; } // Try to infer the template types Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters( fnType, n); if (inferred.size() > 0) { // Something useful was found, try to replace it. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return true; } return false; } private

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> = rightScope; } /** * Gets the safe estimated scope without knowing if all of the * subexpressions will be evaluated. */ FlowScope getJoinedFlowScope() { if (joinedScope == null) { if (leftScope == rightScope) { joinedScope = rightScope; } else { joinedScope = join(leftScope, rightScope); } } return joinedScope; } /** * Gets the outcome scope if we do know the outcome of the entire * expression. */ FlowScope getOutcomeFlowScope(int nodeType, boolean outcome) { if (nodeType == Token.AND && outcome || nodeType == Token.OR && !outcome) { // We know that the whole expression must have executed. return rightScope; } else { return getJoinedFlowScope(); } } } private BooleanOutcomePair newBooleanOutcomePair( JSType jsType, FlowScope flowScope) { if (jsType == null) { return new BooleanOutcomePair( BooleanLiteralSet.BOTH, BooleanLiteralSet.BOTH, flowScope, flowScope); } return new BooleanOutcomePair(jsType.getPossibleToBooleanOutcomes(), registry.getNativeType(BOOLEAN_TYPE).isSubtype(jsType) ? BooleanLiteralSet.BOTH : BooleanLiteralSet.EMPTY, flowScope, flowScope); } private void redeclareSimpleVar( FlowScope scope, Node nameNode, JSType varType) { Preconditions.checkState(nameNode.isName()); String varName = nameNode.getString(); if (varType == null) { varType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } if (isUnflowable(syntacticScope.getVar(varName))) { return; } scope.inferSlotType(varName, varType); } private boolean isUnflowable(Var v) { return v != null && v.isLocal() && v.isMarkedEscaped() && // It's OK to flow a variable in the scope where it's escaped. v.getScope() == syntacticScope; } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) { traverseRoots(Lists.newArrayList(roots)); } public void traverseRoots(List<Node> roots) { if (roots.isEmpty()) { return; } try { Node scopeRoot = roots.get(0).getParent(); Preconditions.checkState(scopeRoot != null); inputId = NodeUtil.getInputId(scopeRoot); sourceName = ""; curNode = scopeRoot; pushScope(scopeRoot); for (Node root : roots) { Preconditions.checkState(root.getParent() == scopeRoot); traverseBranch(root, scopeRoot); } popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } private static final String MISSING_SOURCE = "[source unknown]"; private String formatNodePosition(Node n) { if (n == null) { return MISSING_SOURCE + "\n"; } int lineNumber = n.getLineno(); int columnNumber = n.getCharno(); String src = compiler.getSourceLine(sourceName, lineNumber); if (src == null) { src = MISSING_SOURCE; } return sourceName + ":" + lineNumber + ":" + columnNumber + "\n" + src + "\n"; } /** * Traverses a parse tree recursively with a scope, starting with the given * root. This should only be used in the global scope. Otherwise, use * {@link #traverseAtScope}. */ void traverseWithScope(Node root, Scope s) { Preconditions.checkState(s.isGlobal()); inputId = null; sourceName = ""; curNode = root; pushScope(s); traverseBranch(root, null); popScope(); } /** * Traverses a parse tree recursively with a scope, starting at that scope's * root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.isFunction()) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. if (inputId == null) { inputId = NodeUtil.getInputId(n); } sourceName = getSourceName(n); cur

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Node = n; pushScope(s); Node args = n.getFirstChild().getNext(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else { traverseWithScope(n, s); } } /** * Traverses an inner node recursively with a refined scope. An inner node may * be any node with a non {@code null} parent (i.e. all nodes except the * root). * * @param node the node to traverse * @param parent the node's parent, it may not be {@code null} * @param refinedScope the refined scope of the scope currently at the top of * the scope stack or in trivial cases that very scope or {@code null} */ protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) { Preconditions.checkNotNull(parent); if (refinedScope != null && getScope() != refinedScope) { curNode = node; pushScope(refinedScope); traverseBranch(node, parent); popScope(); } else { traverseBranch(node, parent); } } /** * Gets the compiler. */ public Compiler getCompiler() { // TODO(nicksantos): Remove this type cast. This is just temporary // while refactoring. return (Compiler) compiler; } /** * Gets the current line number, or zero if it cannot be determined. The line * number is retrieved lazily as a running time optimization. */ public int getLineNumber() { Node cur = curNode; while (cur != null) { int line = cur.getLineno(); if (line >=0) { return line; } cur = cur.getParent(); } return 0; } /** * Gets the current input source name. * * @return A string that may be empty, but not null */ public String getSourceName() { return sourceName; } /** * Gets the current input source. */ public CompilerInput getInput() { return compiler.getInput(inputId); } /** * Gets the current input module. */ public JSModule getModule() {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> CompilerInput input = getInput(); return input == null ? null : input.getModule(); } /** Returns the node currently being traversed. */ public Node getCurrentNode() { return curNode; } /** * Traverses a node recursively. */ public static void traverse( AbstractCompiler compiler, Node root, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverse(root); } /** * Traverses a list of node trees. */ public static void traverseRoots( AbstractCompiler compiler, List<Node> roots, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } public static void traverseRoots( AbstractCompiler compiler, Callback cb, Node ... roots) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } /** * Traverses a branch. */ @SuppressWarnings("fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { inputId = n.getInputId(); sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.isFunction()); final Node fnName = n.getFirstChild(); boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n); if (!isFunctionExpression) { // Functions declarations are in the scope containing the

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.isBlock(), body); traverseBranch(body, n); popScope(); } /** Examines the functions stack for the last instance of a function node. */ @SuppressWarnings("unchecked") public Node getEnclosingFunction() { if (scopes.size() + scopeRoots.size() < 2) { return null; } else { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Node node) { Preconditions.checkState(curNode != null); scopeRoots.push(node); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Scope s) { Preconditions.checkState(curNode != null); scopes.push(s); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Pops back to the previous scope (e.g. when leaving a function). */ private void popScope() { if (scopeCallback != null) { scopeCallback.exitScope(this); } if (scopeRoots.isEmpty()) { scopes.pop(); } else { scopeRoots.pop(); } cfgs.pop(); } /** Gets the current scope. */ public Scope getScope() { Scope scope = scopes.isEmpty() ? null : scopes.peek(); if (scopeRoots.isEmpty()) { return scope; } Iterator<Node> it = scopeRoots.descendingIterator

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import com.google.common.base.Preconditions; import java.util.List; /** * An AST construction helper class * @author johnlenz@google.com (John Lenz) */ public class IR { private IR() {} public static Node empty() { return new Node(Token.EMPTY); } public static Node function(Node name, Node params, Node body) { Preconditions.checkState(name.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>isName()); Preconditions.checkState(params.isParamList()); Preconditions.checkState(body.isBlock()); return new Node(Token.FUNCTION, name, params, body); } public static Node paramList() { return new Node(Token.PARAM_LIST); } public static Node paramList(Node param) { Preconditions.checkState(param.isName()); return new Node(Token.PARAM_LIST, param); } public static Node paramList(Node ... params) { Node paramList = paramList(); for (Node param : params) { Preconditions.checkState(param.isName()); paramList.addChildToBack(param); } return paramList; } public static Node paramList(List<Node> params) { Node paramList = paramList(); for (Node param : params) { Preconditions.checkState(param.isName()); paramList.addChildToBack(param); } return paramList; } public static Node block() { Node block = new Node(Token.BLOCK); return block; } public static Node block(Node stmt) { Preconditions.checkState(mayBeStatement(stmt)); Node block = new Node(Token.BLOCK, stmt); return block; } public static Node block(Node ... stmts) { Node block = block(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatement(stmt)); block.addChildToBack(stmt); } return block; } public static Node block(List<Node> stmts) { Node paramList = block(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatement(stmt)); paramList.addChildToBack(stmt); } return paramList; } private static Node blockUnchecked(Node stmt) { return new Node(Token.BLOCK, stmt); } public static Node script() { // TODO(johnlenz): finish setting up the SCRIPT node Node block = new Node(Token.SCRIPT); return block; } public static Node script(Node ... stmts) { Node block = script(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatementNoReturn(stmt

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>)); block.addChildToBack(stmt); } return block; } public static Node script(List<Node> stmts) { Node paramList = script(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatementNoReturn(stmt)); paramList.addChildToBack(stmt); } return paramList; } public static Node var(Node name, Node value) { Preconditions.checkState(name.isName() && !name.hasChildren()); Preconditions.checkState(mayBeExpression(value)); name.addChildToFront(value); return var(name); } public static Node var(Node name) { Preconditions.checkState(name.isName()); return new Node(Token.VAR, name); } public static Node returnNode() { return new Node(Token.RETURN); } public static Node returnNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.RETURN, expr); } public static Node throwNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.THROW, expr); } public static Node exprResult(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.EXPR_RESULT, expr); } public static Node ifNode(Node cond, Node then) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); return new Node(Token.IF, cond, then); } public static Node ifNode(Node cond, Node then, Node elseNode) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); Preconditions.checkState(elseNode.isBlock()); return new Node(Token.IF, cond, then, elseNode); } public static Node doNode(Node body, Node cond) { Preconditions.checkState(body.isBlock()); Preconditions.checkState(mayBeExpression(cond)); return new Node(Token.DO, body, cond); } public static Node forIn(Node target, Node cond, Node body) { Preconditions.checkState(target.isVar() ||

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> mayBeExpression(target)); Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(body.isBlock()); return new Node(Token.FOR, target, cond, body); } public static Node forNode(Node init, Node cond, Node incr, Node body) { Preconditions.checkState(init.isVar() || mayBeExpressionOrEmpty(init)); Preconditions.checkState(mayBeExpressionOrEmpty(cond)); Preconditions.checkState(mayBeExpressionOrEmpty(incr)); Preconditions.checkState(body.isBlock()); return new Node(Token.FOR, init, cond, incr, body); } public static Node switchNode(Node cond, Node ... cases) { Preconditions.checkState(mayBeExpression(cond)); Node switchNode = new Node(Token.SWITCH, cond); for (Node caseNode : cases) { Preconditions.checkState(caseNode.isCase() || caseNode.isDefaultCase()); switchNode.addChildToBack(caseNode); } return switchNode; } public static Node caseNode(Node expr, Node body) { Preconditions.checkState(mayBeExpression(expr)); Preconditions.checkState(body.isBlock()); body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); return new Node(Token.CASE, expr, body); } public static Node defaultCase(Node body) { Preconditions.checkState(body.isBlock()); body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); return new Node(Token.DEFAULT_CASE, body); } public static Node label(Node name, Node stmt) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); Preconditions.checkState(mayBeStatement(stmt)); Node block = new Node(Token.LABEL, name, stmt); return block; } public static Node labelName(String name) { Preconditions.checkState(!name.isEmpty()); return Node.newString(Token.LABEL_NAME, name); } public static Node tryFinally(Node tryBody, Node finallyBody) { Preconditions.checkState(tryBody.isBlock()); Preconditions.checkState(finallyBody.isBlock());

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Node catchBody = block().copyInformationFrom(tryBody); return new Node(Token.TRY, tryBody, catchBody, finallyBody); } public static Node tryCatch(Node tryBody, Node catchNode) { Preconditions.checkState(tryBody.isBlock()); Preconditions.checkState(catchNode.isCatch()); Node catchBody = blockUnchecked(catchNode).copyInformationFrom(catchNode); return new Node(Token.TRY, tryBody, catchBody); } public static Node tryCatchFinally( Node tryBody, Node catchNode, Node finallyBody) { Preconditions.checkState(finallyBody.isBlock()); Node tryNode = tryCatch(tryBody, catchNode); tryNode.addChildToBack(finallyBody); return tryNode; } public static Node catchNode(Node expr, Node body) { Preconditions.checkState(expr.isName()); Preconditions.checkState(body.isBlock()); return new Node(Token.CATCH, expr, body); } public static Node breakNode() { return new Node(Token.BREAK); } public static Node breakNode(Node name) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); return new Node(Token.BREAK, name); } public static Node continueNode() { return new Node(Token.CONTINUE); } public static Node continueNode(Node name) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); return new Node(Token.CONTINUE, name); } // public static Node call(Node target, Node ... args) { Node call = new Node(Token.CALL, target); for (Node arg : args) { Preconditions.checkState(mayBeExpression(arg)); call.addChildToBack(arg); } return call; } public static Node newNode(Node target, Node ... args) { Node newcall = new Node(Token.NEW, target); for (Node arg : args) { Preconditions.checkState(mayBeExpression(arg)); newcall.addChildToBack(arg); } return newcall; } public static Node name(String name) { return Node.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>newString(Token.NAME, name); } public static Node getprop(Node target, Node prop) { Preconditions.checkState(mayBeExpression(target)); Preconditions.checkState(prop.isString()); return new Node(Token.GETPROP, target, prop); } public static Node getelem(Node target, Node elem) { Preconditions.checkState(mayBeExpression(target)); Preconditions.checkState(mayBeExpression(elem)); return new Node(Token.GETELEM, target, elem); } public static Node assign(Node target, Node expr) { Preconditions.checkState(isAssignmentTarget(target)); Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.ASSIGN, target, expr); } public static Node hook(Node cond, Node trueval, Node falseval) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(mayBeExpression(trueval)); Preconditions.checkState(mayBeExpression(falseval)); return new Node(Token.HOOK, cond, trueval, falseval); } public static Node comma(Node expr1, Node expr2) { return binaryOp(Token.COMMA, expr1, expr2); } public static Node and(Node expr1, Node expr2) { return binaryOp(Token.AND, expr1, expr2); } public static Node or(Node expr1, Node expr2) { return binaryOp(Token.OR, expr1, expr2); } public static Node not(Node expr1) { return unaryOp(Token.NOT, expr1); } /** * "==" */ public static Node eq(Node expr1, Node expr2) { return binaryOp(Token.EQ, expr1, expr2); } /** * "===" */ public static Node sheq(Node expr1, Node expr2) { return binaryOp(Token.SHEQ, expr1, expr2); } public static Node voidNode(Node expr1) { return unaryOp(Token.VOID, expr1); } public static Node neg(Node expr1) { return unaryOp(Token.NEG, expr1); } public static Node pos(Node expr1)

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> { return unaryOp(Token.POS, expr1); } public static Node add(Node expr1, Node expr2) { return binaryOp(Token.ADD, expr1, expr2); } public static Node sub(Node expr1, Node expr2) { return binaryOp(Token.SUB, expr1, expr2); } // TODO(johnlenz): the rest of the ops // literals public static Node objectlit(Node ... propdefs) { Node objectlit = new Node(Token.OBJECTLIT); for (Node propdef : propdefs) { Preconditions.checkState( propdef.isStringKey() || propdef.isGetterDef() || propdef.isSetterDef()); Preconditions.checkState(propdef.hasOneChild()); objectlit.addChildToBack(propdef); } return objectlit; } // TODO(johnlenz): quoted props public static Node propdef(Node string, Node value) { Preconditions.checkState(string.isStringKey()); Preconditions.checkState(!string.hasChildren()); Preconditions.checkState(mayBeExpression(value)); string.addChildToFront(value); return string; } public static Node arraylit(Node ... exprs) { Node arraylit = new Node(Token.ARRAYLIT); for (Node expr : exprs) { Preconditions.checkState(mayBeExpressionOrEmpty(expr)); arraylit.addChildToBack(expr); } return arraylit; } public static Node regexp(Node expr) { Preconditions.checkState(expr.isString()); return new Node(Token.REGEXP, expr); } public static Node regexp(Node expr, Node flags) { Preconditions.checkState(expr.isString()); Preconditions.checkState(flags.isString()); return new Node(Token.REGEXP, expr, flags); } public static Node string(String s) { return Node.newString(s); } public static Node stringKey(String s) { return Node.newString(Token.STRING_KEY, s); } public static Node number(double d) { return Node.newNumber(d); } public static Node thisNode() { return new Node(Token.THIS); } public

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> static Node trueNode() { return new Node(Token.TRUE); } public static Node falseNode() { return new Node(Token.FALSE); } public static Node nullNode() { return new Node(Token.NULL); } // helper methods private static Node binaryOp(int token, Node expr1, Node expr2) { Preconditions.checkState(mayBeExpression(expr1)); Preconditions.checkState(mayBeExpression(expr2)); return new Node(token, expr1, expr2); } private static Node unaryOp(int token, Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(token, expr); } private static boolean mayBeExpressionOrEmpty(Node n) { return n.isEmpty() || mayBeExpression(n); } private static boolean isAssignmentTarget(Node n) { return n.isName() || n.isGetProp() || n.isGetElem(); } // NOTE: some nodes are neither statements nor expression nodes: // SCRIPT, LABEL_NAME, PARAM_LIST, CASE, DEFAULT_CASE, CATCH // GETTER_DEF, SETTER_DEF /** * It isn't possible to always determine if a detached node is a expression, * so make a best guess. */ private static boolean mayBeStatementNoReturn(Node n) { switch (n.getType()) { case Token.EMPTY: case Token.FUNCTION: // EMPTY and FUNCTION are used both in expression and statement // contexts return true; case Token.BLOCK: case Token.BREAK: case Token.CONST: case Token.CONTINUE: case Token.DEBUGGER: case Token.DO: case Token.EXPR_RESULT: case Token.FOR: case Token.IF: case Token.LABEL: case Token.SWITCH: case Token.THROW: case Token.TRY: case Token.VAR: case Token.WHILE: case Token.WITH: return true; default: return false; } } /** * It isn't possible to always determine if a detached node is a expression, * so make a best guess. */ private static boolean mayBeStatement(Node n) { if (!may

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> * Creates a map of String->Node from a map of String->Number/String/Boolean. */ private static Map<String, Node> getReplacementsHelper( Map<String, Object> source) { Map<String, Node> map = Maps.newHashMap(); for (Map.Entry<String, Object> entry : source.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); if (value instanceof Boolean) { map.put(name, NodeUtil.booleanNode(((Boolean) value).booleanValue())); } else if (value instanceof Integer) { map.put(name, IR.number(((Integer) value).intValue())); } else if (value instanceof Double) { map.put(name, IR.number(((Double) value).doubleValue())); } else { Preconditions.checkState(value instanceof String); map.put(name, IR.string((String) value)); } } return map; } /** * Sets the value of the {@code @define} variable in JS * to a boolean literal. */ public void setDefineToBooleanLiteral(String defineName, boolean value) { defineReplacements.put(defineName, new Boolean(value)); } /** * Sets the value of the {@code @define} variable in JS to a * String literal. */ public void setDefineToStringLiteral(String defineName, String value) { defineReplacements.put(defineName, value); } /** * Sets the value of the {@code @define} variable in JS to a * number literal. */ public void setDefineToNumberLiteral(String defineName, int value) { defineReplacements.put(defineName, new Integer(value)); } /** * Sets the value of the {@code @define} variable in JS to a * number literal. */ public void setDefineToDoubleLiteral(String defineName, double value) { defineReplacements.put(defineName, new Double(value)); } /** * Sets the value of the tweak in JS * to a boolean literal. */ public void setTweakToBooleanLiteral(String tweakId, boolean value) { tweakReplacements.put(tweakId, new Boolean(value)); } /**

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>boolean value) { this.chainCalls = value; } /** * If true, accept `const' keyword. */ public void setAcceptConstKeyword(boolean value) { this.acceptConstKeyword = value; } /** * Enable run-time type checking, which adds JS type assertions for debugging. * * @param logFunction A JS function to be used for logging run-time type * assertion failures. */ public void enableRuntimeTypeCheck(String logFunction) { this.runtimeTypeCheck = true; this.runtimeTypeCheckLogFunction = logFunction; } public void disableRuntimeTypeCheck() { this.runtimeTypeCheck = false; } public void setGenerateExports(boolean generateExports) { this.generateExports = generateExports; } public void setCodingConvention(CodingConvention codingConvention) { this.codingConvention = codingConvention; } public CodingConvention getCodingConvention() { return codingConvention; } /** * Sets dependency options. See the DependencyOptions class for more info. * This supersedes manageClosureDependencies. */ public void setDependencyOptions(DependencyOptions options) { Preconditions.checkNotNull(options); this.dependencyOptions = options; } /** * Sort inputs by their goog.provide/goog.require calls, and prune inputs * whose symbols are not required. */ public void setManageClosureDependencies(boolean newVal) { dependencyOptions.setDependencySorting( newVal || dependencyOptions.shouldSortDependencies()); dependencyOptions.setDependencyPruning( newVal || dependencyOptions.shouldPruneDependencies()); dependencyOptions.setMoocherDropping(false); manageClosureDependencies = newVal; } /** * Sort inputs by their goog.provide/goog.require calls. * * @param entryPoints Entry points to the program. Must be goog.provide'd * symbols. Any goog.provide'd symbols that are not a transitive * dependency of the entry points will be deleted. * Files without goog.provides, and their dependencies, * will always be left in. */ public void setManageClosureDependencies(List<String> entryPoints) { Preconditions.checkNotNull(entryPoints); setManageClosureDependencies(true); dependencyOptions.setEntryPoints

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> root); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Note: Constant properties annotations are not propagated. if (n.isName()) { if (n.getString().isEmpty()) { return; } JSDocInfo info = null; // Find the JSDocInfo for a top-level variable. Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } boolean shouldBeConstant = (info != null && info.isConstant()) || NodeUtil.isConstantByConvention( compiler.getCodingConvention(), n, parent); boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (shouldBeConstant && !isMarkedConstant) { if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " parent:" + n.getParent().toStringTree()); } n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } } /** * Walk the AST tree and verify that constant names are used consistently. */ static class VerifyConstants extends AbstractPostOrderCallback implements CompilerPass { final private AbstractCompiler compiler; final private boolean checkUserDeclarations; VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) { this.compiler = compiler; this.checkUserDeclarations = checkUserDeclarations; } @Override public void process(Node externs, Node root) { Node externsAndJs = root.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState(externsAndJs.hasChild(externs)); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } private Map<String, Boolean> constantMap = Maps.newHashMap(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { String name = n.getString(); if (n.getString().isEmpty()) { return; } boolean isConst = n.getBooleanProp(

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Node.IS_CONSTANT_NAME); if (checkUserDeclarations) { boolean expectedConst = false; CodingConvention convention = compiler.getCodingConvention(); if (NodeUtil.isConstantName(n) || NodeUtil.isConstantByConvention(convention, n, parent)) { expectedConst = true; } else { expectedConst = false; JSDocInfo info = null; Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if (info != null && info.isConstant()) { expectedConst = true; } else { expectedConst = false; } } if (expectedConst) { Preconditions.checkState(expectedConst == isConst, "The name %s is not annotated as constant.", name); } else { Preconditions.checkState(expectedConst == isConst, "The name %s should not be annotated as constant.", name); } } Boolean value = constantMap.get(name); if (value == null) { constantMap.put(name, isConst); } else { Preconditions.checkState(value.booleanValue() == isConst, "The name %s is not consistently annotated as constant.", name); } } } } /** * Simplify the AST: * - VAR declarations split, so they represent exactly one child * declaration. * - WHILEs are converted to FORs * - FOR loop are initializers are moved out of the FOR structure * - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are * moved into a block. * - Add constant annotations based on coding convention. */ static class NormalizeStatements implements Callback { private final AbstractCompiler compiler; private final boolean assertOnChange; NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) { this.compiler = compiler; this.assertOnChange = assertOnChange; } private void reportCodeChange(String changeDescription) { if (assertOnChange) { throw new IllegalStateException( "Normalize constraints violated:\n" + changeDescription); } compiler.reportCodeChange(); } @Override public boolean shouldTraverse(

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); Node empty = IR.empty(); empty.copyInformationFrom(n); n.addChildBefore(empty, expr); n.addChildAfter(empty.cloneNode(), expr); reportCodeChange("WHILE node"); } break; case Token.FUNCTION: normalizeFunctionDeclaration(n); break; case Token.NAME: case Token.STRING: case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: if (!compiler.getLifeCycleStage().isNormalizedObfuscated()) { annotateConstantsByConvention(n, parent); } break; } } /** * Mark names and properties that are constants by convention. */ private void annotateConstantsByConvention(Node n, Node parent) { Preconditions.checkState( n.isName() || n.isString() || n.isStringKey() || n.isGetterDef() || n.isSetterDef()); // There are only two cases where a string token // may be a variable reference: The right side of a GETPROP // or an OBJECTLIT key. boolean isObjLitKey = NodeUtil.isObjectLitKey(n, parent); boolean isProperty = isObjLitKey || (parent.isGetProp() && parent.getLastChild() == n); if (n.isName() || isProperty) { boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (!isMarkedConstant && NodeUtil.isConstantByConvention( compiler.getCodingConvention(), n, parent)) { if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " parent:" + n.getParent().toStringTree()); } n.put

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>BooleanProp(Node.IS_CONSTANT_NAME, true); } } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ private void normalizeFunctionDeclaration(Node n) { Preconditions.checkState(n.isFunction()); if (!NodeUtil.isFunctionExpression(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { rewriteFunctionDeclaration(n); } } /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ private void rewriteFunctionDeclaration(Node n) { // Prepare a spot for the function. Node oldNameNode = n.getFirstChild(); Node fnNameNode = oldNameNode.cloneNode(); Node var = IR.var(fnNameNode).srcref(n); // Prepare the function oldNameNode.setString(""); // Move the function Node parent = n.getParent(); parent.replaceChild(n, var); fnNameNode.addChildToFront(n); reportCodeChange("Function declaration"); } /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.isLabel()) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.isLabel()) { extractForInitializer(n, null, null); } // Only inspect the children of SCRIPTs, BLOCKs, as all these // are the only legal place for VARs. if (NodeUtil.isStatementBlock(n)) { splitVarDeclarations(n); } if (n.isFunction()) { move

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>NamedFunctions(n.getLastChild()); } } // TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are // fixed. /** * Limit the number of special cases where LABELs need to be handled. Only * BLOCK and loops are allowed to be labeled. Loop labels must remain in * place as the named continues are not allowed for labeled blocks. */ private void normalizeLabels(Node n) { Preconditions.checkArgument(n.isLabel()); Node last = n.getLastChild(); switch (last.getType()) { case Token.LABEL: case Token.BLOCK: case Token.FOR: case Token.WHILE: case Token.DO: return; default: Node block = IR.block(); block.copyInformationFrom(last); n.replaceChild(last, block); block.addChildToFront(last); reportCodeChange("LABEL normalization"); return; } } /** * Bring the initializers out of FOR loops. These need to be placed * before any associated LABEL nodes. This needs to be done from the top * level label first so this is called as a pre-order callback (from * shouldTraverse). * * @param n The node to inspect. * @param before The node to insert the initializer before. * @param beforeParent The parent of the node before which the initializer * will be inserted. */ private void extractForInitializer( Node n, Node before, Node beforeParent) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); Node insertBefore = (before == null) ? c : before; Node insertBeforeParent = (before == null) ? n : beforeParent; switch (c.getType()) { case Token.LABEL: extractForInitializer(c, insertBefore, insertBeforeParent); break; case Token.FOR: if (NodeUtil.isForIn(c)) { Node first = c.getFirstChild(); if (first.isVar()) { // Transform: // for (var a = 1 in b) {} // to: // var a = 1; for (a in b) {}; Node

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> newStatement = first; // Clone just the node, to remove any initialization. Node name = newStatement.getFirstChild().cloneNode(); first.getParent().replaceChild(first, name); insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR-IN var declaration"); } } else if (!c.getFirstChild().isEmpty()) { Node init = c.getFirstChild(); Node empty = IR.empty(); empty.copyInformationFrom(c); c.replaceChild(init, empty); Node newStatement; // Only VAR statements, and expressions are allowed, // but are handled differently. if (init.isVar()) { newStatement = init; } else { newStatement = NodeUtil.newExpr(init); } insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR initializer"); } break; } } } /** * Split a var node such as: * var a, b; * into individual statements: * var a; * var b; * @param n The whose children we should inspect. */ private void splitVarDeclarations(Node n) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); if (c.isVar()) { if (assertOnChange && !c.hasChildren()) { throw new IllegalStateException("Empty VAR node."); } while (c.getFirstChild() != c.getLastChild()) { Node name = c.getFirstChild(); c.removeChild(name); Node newVar = IR.var(name).srcref(n); n.addChildBefore(newVar, c); reportCodeChange("VAR with multiple children"); } } } } /** * Move all the functions that are valid at the execution of the first * statement of the function to the beginning of the function definition. */ private void moveNamedFunctions(Node functionBody) { Preconditions.checkState( functionBody.getParent().isFunction()); Node previous = null; Node current = functionBody.getFirstChild(); // Skip any declarations at the beginning of the function body, they // are already in the right place. while (current

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> != null && NodeUtil.isFunctionDeclaration(current)) { previous = current; current = current.getNext(); } // Find any remaining declarations and move them. Node insertAfter = previous; while (current != null) { // Save off the next node as the current node maybe removed. Node next = current.getNext(); if (NodeUtil.isFunctionDeclaration(current)) { // Remove the declaration from the body. Preconditions.checkNotNull(previous); functionBody.removeChildAfter(previous); // Read the function at the top of the function body (after any // previous declarations). insertAfter = addToFront(functionBody, current, insertAfter); reportCodeChange("Move function declaration not at top of function"); } else { // Update the previous only if the current node hasn't been moved. previous = current; } current = next; } } /** * @param after The child node to insert the newChild after, or null if * newChild should be added to the front of parent's child list. * @return The inserted child node. */ private Node addToFront(Node parent, Node newChild, Node after) { if (after == null) { parent.addChildToFront(newChild); } else { parent.addChildAfter(newChild, after); } return newChild; } } /** * Remove duplicate VAR declarations. */ private void removeDuplicateDeclarations(Node externs, Node root) { Callback tickler = new ScopeTicklingCallback(); ScopeCreator scopeCreator = new SyntacticScopeCreator( compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverseRoots(externs, root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler implements SyntacticScopeCreator.RedeclarationHandler { private Set<Var> hasOkDuplicateDeclaration = Sets.newHashSet(); /** * Remove duplicate VAR declarations encountered discovered during * scope creation. */ @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); Var v = s.getVar(name);

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> if (v != null && s.isGlobal()) { // We allow variables to be duplicate declared if one // declaration appears in source and the other in externs. // This deals with issues where a browser built-in is declared // in one browser but not in another. if (v.isExtern() && !input.isExtern()) { if (hasOkDuplicateDeclaration.add(v)) { return; } } } // If name is "arguments", Var maybe null. if (v != null && v.getParentNode().isCatch()) { // Redeclaration of a catch expression variable is hard to model // without support for "with" expressions. // The ECMAScript spec (section 12.14), declares that a catch // "catch (e) {}" is handled like "with ({'e': e}) {}" so that // "var e" would refer to the scope variable, but any following // reference would still refer to "e" of the catch expression. // Until we have support for this disallow it. // Currently the Scope object adds the catch expression to the // function scope, which is technically not true but a good // approximation for most uses. // TODO(johnlenz): Consider improving how scope handles catch // expression. // Use the name of the var before it was made unique. name = MakeDeclaredNamesUnique.ContextualRenameInverter.getOrginalName( name); compiler.report( JSError.make( input.getName(), n, CATCH_BLOCK_VAR_ERROR, name)); } else if (v != null && parent.isFunction()) { if (v.getParentNode().isVar()) { s.undeclare(v); s.declare(name, n, n.getJSType(), v.input); replaceVarWithAssignment(v.getNameNode(), v.getParentNode(), v.getParentNode().getParent()); } } else if (parent.isVar()) { Preconditions.checkState(parent.hasOneChild()); replaceVarWithAssignment(n, parent, parent.getParent()); } } /** * Remove the parent VAR. There are three cases that need to be handled: * 1) "var a = b;" which is replaced

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> with "a = b" * 2) "label:var a;" which is replaced with "label:;". Ideally, the * label itself would be removed but that is not possible in the * context in which "onRedeclaration" is called. * 3) "for (var a in b) ..." which is replaced with "for (a in b)..." * Cases we don't need to handle are VARs with multiple children, * which have already been split into separate declarations, so there * is no need to handle that here, and "for (var a;;);", which has * been moved out of the loop. * The result of this is that in each case the parent node is replaced * which is generally dangerous in a traversal but is fine here with * the scope creator, as the next node of interest is the parent's * next sibling. */ private void replaceVarWithAssignment(Node n, Node parent, Node gramps) { if (n.hasChildren()) { // The * is being initialize, preserve the new value. parent.removeChild(n); // Convert "var name = value" to "name = value" Node value = n.getFirstChild(); n.removeChild(value); Node replacement = IR.assign(n, value); replacement.copyInformationFrom(parent); gramps.replaceChild(parent, NodeUtil.newExpr(replacement)); } else { // It is an empty reference remove it. if (NodeUtil.isStatementBlock(gramps)) { gramps.removeChild(parent); } else if (gramps.isFor()) { // This is the "for (var a in b)..." case. We don't need to worry // about initializers in "for (var a;;)..." as those are moved out // as part of the other normalizations. parent.removeChild(n); gramps.replaceChild(parent, n); } else { Preconditions.checkState(gramps.isLabel()); // We should never get here. LABELs with a single VAR statement should // already have been normalized to have a BLOCK. throw new IllegalStateException("Unexpected LABEL"); } } reportCodeChange("Duplicate

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> * @param externsRoot The root of the externs parse tree. * @param jsRoot The root of the input parse tree to be checked. */ @Override public void process(Node externsRoot, Node jsRoot) { Node externsAndJs = jsRoot.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState( externsRoot == null || externsAndJs.hasChild(externsRoot)); inferTypes(externsAndJs); } /** Entry point for type inference when running over part of the tree. */ void inferTypes(Node node) { NodeTraversal inferTypes = new NodeTraversal( compiler, new TypeInferringCallback(), scopeCreator); inferTypes.traverseWithScope(node, topScope); } void inferTypes(NodeTraversal t, Node n, Scope scope) { TypeInference typeInference = new TypeInference( compiler, computeCfg(n), reverseInterpreter, scope, assertionFunctionsMap); try { typeInference.analyze(); // Resolve any new type names found during the inference. compiler.getTypeRegistry().resolveTypesInScope(scope); } catch (DataFlowAnalysis.MaxIterationsExceededException e) { compiler.report(t.makeError(n, DATAFLOW_ERROR)); } } private class TypeInferringCallback extends AbstractScopedCallback { @Override public void enterScope(NodeTraversal t) { inferTypes(t, t.getCurrentNode(), t.getScope()); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Do nothing } } private ControlFlowGraph<Node> computeCfg(Node n) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false); cfa.process(null, n); return cfa.getCfg(); } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> + i, expected[i])); } compiler.init(externsInputs, inputs, getOptions()); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); Node externsRoot = root.getFirstChild(); Node mainRoot = externsRoot.getNext(); // Only run the normalize pass, if asked. if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); } return mainRoot; } protected Node parseExpectedJs(String expected) { return parseExpectedJs(new String[] {expected}); } /** * Generates a list of modules from a list of inputs, such that each module * depends on the module before it. */ static JSModule[] createModuleChain(String... inputs) { JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[i - 1]); } return modules; } /** * Generates a list of modules from a list of inputs, such that each module * depends on the first module. */ static JSModule[] createModuleStar(String... inputs) { JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[0]); } return modules; } /** * Generates a list of modules from a list of inputs, such that modules * form a bush formation. In a bush formation, module 2 depends * on module 1, and all other modules depend on module 2. */ static JSModule[] createModuleBush(String ... inputs) { Preconditions.checkState(inputs.length > 2); JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[i == 1 ? 0 : 1]); } return modules; } /** * Generates a list of modules from a list

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> So once a child scope is created, this flow scope may not be modified. private boolean frozen = false; // The last slot defined in this flow instruction, and the head of the // linked list of slots. private LinkedFlowSlot lastSlot; private LinkedFlowScope(FlatFlowScopeCache cache, LinkedFlowScope directParent) { this.cache = cache; if (directParent == null) { this.lastSlot = null; this.depth = 0; this.parent = cache.linkedEquivalent; } else { this.lastSlot = directParent.lastSlot; this.depth = directParent.depth + 1; this.parent = directParent; } } LinkedFlowScope(FlatFlowScopeCache cache) { this(cache, null); } LinkedFlowScope(LinkedFlowScope directParent) { this(directParent.cache, directParent); } /** Gets the function scope for this flow scope. */ private Scope getFunctionScope() { return cache.functionScope; } /** Whether this flows from a bottom scope. */ private boolean flowsFromBottom() { return getFunctionScope().isBottom(); } /** * Creates an entry lattice for the flow. */ public static LinkedFlowScope createEntryLattice(Scope scope) { return new LinkedFlowScope(new FlatFlowScopeCache(scope)); } @Override public void inferSlotType(String symbol, JSType type) { Preconditions.checkState(!frozen); lastSlot = new LinkedFlowSlot(symbol, type, lastSlot); depth++; cache.dirtySymbols.add(symbol); } @Override public void inferQualifiedSlot(Node node, String symbol, JSType bottomType, JSType inferredType) { Scope functionScope = getFunctionScope(); if (functionScope.isLocal()) { if (functionScope.getVar(symbol) == null && !functionScope.isBottom()) { functionScope.declare(symbol, node, bottomType, null); } inferSlotType(symbol, inferredType); } } @Override public JSType getTypeOfThis() { return cache.functionScope.getTypeOfThis(); } @Override public Node getRootNode() { return getFunctionScope().getRootNode();

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> both 0 based. int lineBaseOffset = 1; if (generator instanceof SourceMapGeneratorV1 || generator instanceof SourceMapGeneratorV2) { lineBaseOffset = 0; } generator.addMapping( sourceFile, originalName, new FilePosition(node.getLineno() - lineBaseOffset, node.getCharno()), outputStartPosition, outputEndPosition); } /** * @param sourceFile The source file location to fixup. * @return a remapped source file. */ private String fixupSourceLocation(String sourceFile) { if (prefixMappings.isEmpty()) { return sourceFile; } String fixed = sourceLocationFixupCache.get(sourceFile); if (fixed != null) { return fixed; } // Replace the first prefix found with its replacement for (LocationMapping mapping : prefixMappings) { if (sourceFile.startsWith(mapping.prefix)) { fixed = mapping.replacement + sourceFile.substring( mapping.prefix.length()); break; } } // If none of the mappings match then use the original file path. if (fixed == null) { fixed = sourceFile; } sourceLocationFixupCache.put(sourceFile, fixed); return fixed; } public void appendTo(Appendable out, String name) throws IOException { generator.appendTo(out, name); } public void reset() { generator.reset(); sourceLocationFixupCache.clear(); } public void setStartingPosition(int offsetLine, int offsetIndex) { generator.setStartingPosition(offsetLine, offsetIndex); } public void setWrapperPrefix(String prefix) { generator.setWrapperPrefix(prefix); } public void validate(boolean validate) { generator.validate(validate); } /** * @param sourceMapLocationMappings */ public void setPrefixMappings(List<LocationMapping> sourceMapLocationMappings) { this.prefixMappings = sourceMapLocationMappings; } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> reference is resolved. * * The {@code UnresolvedType} will behave like an opaque unknown type. * When its {@code #resolve} method is called, it will return the underlying * type. The underlying type can resolve to any JS type.<p> * * @author nicksantos@google.com (Nick Santos) */ class UnresolvedTypeExpression extends UnknownType { private static final long serialVersionUID = 1L; private final Node typeExpr; private final String sourceName; /** * Create a named type based on the reference. */ UnresolvedTypeExpression(JSTypeRegistry registry, Node typeExpr, String sourceName) { super(registry, false); Preconditions.checkNotNull(typeExpr); this.typeExpr = typeExpr; this.sourceName = sourceName; } /** * Resolve the referenced type within the enclosing scope. */ @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> enclosing) { return registry.createFromTypeNodes(typeExpr, sourceName, enclosing); } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> modules; /** * Lists of modules at each depth. <code>modulesByDepth.get(3)</code> is a * list of the modules at depth 3, for example. */ private List<List<JSModule>> modulesByDepth; /** * dependencyMap is a cache of dependencies that makes the dependsOn * function faster. Each map entry associates a starting * JSModule with the set of JSModules that are transitively dependent on the * starting module. * * If the cache returns null, then the entry hasn't been filled in for that * module. * * dependencyMap should be filled from leaf to root so that * getTransitiveDepsDeepestFirst can use its results directly. */ private Map<JSModule, Set<JSModule>> dependencyMap = Maps.newHashMap(); /** * Creates a module graph from a list of modules in dependency order. */ public JSModuleGraph(JSModule[] modulesInDepOrder) { this(ImmutableList.copyOf(modulesInDepOrder)); } /** * Creates a module graph from a list of modules in dependency order. */ public JSModuleGraph(List<JSModule> modulesInDepOrder) { Preconditions.checkState( modulesInDepOrder.size() == Sets.newHashSet(modulesInDepOrder).size(), "Found duplicate modules"); modules = ImmutableList.copyOf(modulesInDepOrder); modulesByDepth = Lists.newArrayList(); for (JSModule module : modulesInDepOrder) { int depth = 0; for (JSModule dep : module.getDependencies()) { int depDepth = dep.getDepth(); if (depDepth < 0) { throw new ModuleDependenceException(String.format( "Modules not in dependency order: %s preceded %s", module.getName(), dep.getName()), module, dep); } depth = Math.max(depth, depDepth + 1); } module.setDepth(depth); if (depth == modulesByDepth.size()) { modulesByDepth.add(new ArrayList<JSModule>()); } modulesByDepth.get(depth).add(module); } } /** * Gets an iterable over all modules in dependency order. */ Iterable<JSModule

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>ing(true); depOptions.setEntryPoints(entryPoints); return manageDependencies(depOptions, inputs); } /** * Apply the dependency options to the list of sources, returning a new * source list re-ordering and dropping files as necessary. * This module graph will be updated to reflect the new list. * * @param inputs The original list of sources. Used to ensure that the sort * is stable. * @throws CircularDependencyException if there is a circular dependency * between the provides and requires. * @throws MissingProvideException if an entry point was not provided * by any of the inputs. * @see DependencyOptions for more info on how this works. */ public List<CompilerInput> manageDependencies( DependencyOptions depOptions, List<CompilerInput> inputs) throws CircularDependencyException, MissingProvideException { SortedDependencies<CompilerInput> sorter = new SortedDependencies<CompilerInput>(inputs); Set<CompilerInput> entryPointInputs = Sets.newLinkedHashSet(); if (depOptions.shouldPruneDependencies()) { if (!depOptions.shouldDropMoochers()) { entryPointInputs.addAll(sorter.getInputsWithoutProvides()); } for (String entryPoint : depOptions.getEntryPoints()) { entryPointInputs.add(sorter.getInputProviding(entryPoint)); } CompilerInput baseJs = sorter.maybeGetInputProviding("goog"); if (baseJs != null) { entryPointInputs.add(baseJs); } } else { entryPointInputs.addAll(inputs); } // The order of inputs, sorted independently of modules. List<CompilerInput> absoluteOrder = sorter.getDependenciesOf(inputs, depOptions.shouldSortDependencies()); // Figure out which sources *must* be in each module. ListMultimap<JSModule, CompilerInput> entryPointInputsPerModule = LinkedListMultimap.create(); for (CompilerInput input : entryPointInputs) { JSModule module = input.getModule(); Preconditions.checkNotNull(module); entryPointInputsPerModule.put(module, input); } // Clear the modules of their inputs. This also nulls out // the input's reference to its module. for (JSModule module : getAllModules()) { module.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>MODULE_DEPENDENCY", "module {0} cannot reference {2}, defined in " + "module {1}"); static final DiagnosticType NAME_REFERENCE_IN_EXTERNS_ERROR = DiagnosticType.warning( "JSC_NAME_REFERENCE_IN_EXTERNS", "accessing name {0} in externs has no effect"); static final DiagnosticType UNDEFINED_EXTERN_VAR_ERROR = DiagnosticType.warning( "JSC_UNDEFINED_EXTERN_VAR_ERROR", "name {0} is not undefined in the externs."); private Node synthesizedExternsRoot = null; // Vars that still need to be declared in externs. These will be declared // at the end of the pass, or when we see the equivalent var declared // in the normal code. private final Set<String> varsToDeclareInExterns = Sets.newHashSet(); private final AbstractCompiler compiler; // Whether this is the post-processing sanity check. private final boolean sanityCheck; // Whether extern checks emit error. private final boolean strictExternCheck; VarCheck(AbstractCompiler compiler) { this(compiler, false); } VarCheck(AbstractCompiler compiler, boolean sanityCheck) { this.compiler = compiler; this.strictExternCheck = compiler.getErrorLevel( JSError.make("", 0, 0, UNDEFINED_EXTERN_VAR_ERROR)) == CheckLevel.ERROR; this.sanityCheck = sanityCheck; } @Override public void process(Node externs, Node root) { // Don't run externs-checking in sanity check mode. Normalization will // remove duplicate VAR declarations, which will make // externs look like they have assigns. if (!sanityCheck) { NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck()); } NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); for (String varName : varsToDeclareInExterns) { createSynthesizedExternVar(varName); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { Preconditions.checkState(scriptRoot.isScript()); NodeTraversal t = new NodeTraversal(compiler, this

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>); // Note we use the global scope to prevent wrong "undefined-var errors" on // variables that are defined in other JS files. t.traverseWithScope(scriptRoot, SyntacticScopeCreator.generateUntypedTopScope(compiler)); // TODO(bashir) Check if we need to createSynthesizedExternVar like process. } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isName()) { return; } String varName = n.getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(parent.isFunction()); Preconditions.checkState(NodeUtil.isFunctionExpression(parent)); return; } // Check if this is a declaration for a var that has been declared // elsewhere. If so, mark it as a duplicate. if ((parent.isVar() || NodeUtil.isFunctionDeclaration(parent)) && varsToDeclareInExterns.contains(varName)) { createSynthesizedExternVar(varName); n.addSuppression("duplicate"); } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { // The extern checks are stricter, don't report a second error. if (!strictExternCheck || !t.getInput().isExtern()) { t.report(n, UNDEFINED_VAR_ERROR, varName); } if (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { createSynthesizedExternVar(varName); scope.getGlobalScope().declare(varName, n, null, getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> this.depth = -1; } /** Gets the module name. */ @Override public String getName() { return name; } @Override public List<String> getProvides() { return ImmutableList.<String>of(name); } @Override public List<String> getRequires() { ImmutableList.Builder<String> builder = ImmutableList.builder(); for (JSModule m : deps) { builder.add(m.getName()); } return builder.build(); } @Override public String getPathRelativeToClosureBase() { throw new UnsupportedOperationException(); } /** Adds a source file input to this module. */ public void add(SourceFile file) { add(new CompilerInput(file)); } /** Adds a source file input to this module. */ public void addFirst(SourceFile file) { addFirst(new CompilerInput(file)); } /** Adds a source code input to this module. */ public void add(CompilerInput input) { inputs.add(input); input.setModule(this); } /** * Adds a source code input to this module. Call only if the input might * already be associated with a module. Otherwise, use * add(CompilerInput input). */ void addAndOverrideModule(CompilerInput input) { inputs.add(input); input.overrideModule(this); } /** Adds a source code input to this module. */ public void addFirst(CompilerInput input) { inputs.add(0, input); input.setModule(this); } /** Adds a source code input to this module directly after other. */ public void addAfter(CompilerInput input, CompilerInput other) { Preconditions.checkState(inputs.contains(other)); inputs.add(inputs.indexOf(other), input); input.setModule(this); } /** Adds a dependency on another module. */ public void addDependency(JSModule dep) { Preconditions.checkNotNull(dep); Preconditions.checkState(dep != this); deps.add(dep); } /** Removes an input from this module. */ public void remove(CompilerInput input) { input.setModule(null); inputs.remove(input); } /** Removes all of the inputs from this module

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Can be subclassed to apply checks or * store additional state when adding. */ static class ReferenceCollection implements Iterable<Reference> { List<Reference> references = Lists.newArrayList(); @Override public Iterator<Reference> iterator() { return references.iterator(); } void add(Reference reference, NodeTraversal t, Var v) { references.add(reference); } /** * Determines if the variable for this reference collection is * "well-defined." A variable is well-defined if we can prove at * compile-time that it's assigned a value before it's used. * * Notice that if this function returns false, this doesn't imply that the * variable is used before it's assigned. It just means that we don't * have enough information to make a definitive judgment. */ protected boolean isWellDefined() { int size = references.size(); if (size == 0) { return false; } // If this is a declaration that does not instantiate the variable, // it's not well-defined. Reference init = getInitializingReference(); if (init == null) { return false; } Preconditions.checkState(references.get(0).isDeclaration()); BasicBlock initBlock = init.getBasicBlock(); for (int i = 1; i < size; i++) { if (!initBlock.provablyExecutesBefore( references.get(i).getBasicBlock())) { return false; } } return true; } /** * Whether the variable is escaped into an inner scope. */ boolean isEscaped() { Scope scope = null; for (Reference ref : references) { if (scope == null) { scope = ref.scope; } else if (scope != ref.scope) { return true; } } return false; } /** * @param index The index into the references array to look for an * assigning declaration. * * This is either the declaration if a value is assigned (such as * "var a = 2", "function a()...", "... catch (a)..."). */ private boolean isInitializingDeclarationAt(int index) { Reference maybeInit = references.get(index); if (maybeInit.is

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>InitializingDeclaration()) { // This is a declaration that represents the initial value. // Specifically, var declarations without assignments such as "var a;" // are not. return true; } return false; } /** * @param index The index into the references array to look for an * initialized assignment reference. That is, an assignment immediately * follow a variable declaration that itself does not initialize the * variable. */ private boolean isInitializingAssignmentAt(int index) { if (index < references.size() && index > 0) { Reference maybeDecl = references.get(index-1); if (maybeDecl.isVarDeclaration()) { Preconditions.checkState(!maybeDecl.isInitializingDeclaration()); Reference maybeInit = references.get(index); if (maybeInit.isSimpleAssignmentToName()) { return true; } } } return false; } /** * @return The reference that provides the value for the variable at the * time of the first read, if known, otherwise null. * * This is either the variable declaration ("var a = ...") or first * reference following the declaration if it is an assignment. */ Reference getInitializingReference() { if (isInitializingDeclarationAt(0)) { return references.get(0); } else if (isInitializingAssignmentAt(1)) { return references.get(1); } return null; } /** * Constants are allowed to be defined after their first use. */ Reference getInitializingReferenceForConstants() { int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. for (BasicBlock block = ref.getBasicBlock(); block != null; block = block.getParent()) { if (block.isFunction) { break;

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> report; return this; } /** * Main entry point for this phase of processing. This follows the pattern for * JSCompiler phases. * * @param externsRoot The root of the externs parse tree. * @param jsRoot The root of the input parse tree to be checked. */ @Override public void process(Node externsRoot, Node jsRoot) { Preconditions.checkNotNull(scopeCreator); Preconditions.checkNotNull(topScope); Node externsAndJs = jsRoot.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState( externsRoot == null || externsAndJs.hasChild(externsRoot)); if (externsRoot != null) { check(externsRoot, true); } check(jsRoot, false); } /** Main entry point of this phase for testing code. */ public Scope processForTesting(Node externsRoot, Node jsRoot) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } private void checkNoTypeCheckSection(Node n, boolean enterSection) { switch (n.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.VAR: case Token.FUNCTION: case Token.ASSIGN: JSDocInfo info

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>JSDocInfo(prop); if (docInfo != null && docInfo.isImplicitCast()) { return true; } } return false; } /** * Given a constructor type and a property name, check that the property has * the JSDoc annotation @override iff the property is declared on a * superclass. Several checks regarding inheritance correctness are also * performed. */ private void checkDeclaredPropertyInheritance( NodeTraversal t, Node n, FunctionType ctorType, String propertyName, JSDocInfo info, JSType propertyType) { // If the supertype doesn't resolve correctly, we've warned about this // already. if (hasUnknownOrEmptySupertype(ctorType)) { return; } FunctionType superClass = ctorType.getSuperClassConstructor(); boolean superClassHasProperty = superClass != null && superClass.getInstanceType().hasProperty(propertyName); boolean superClassHasDeclaredProperty = superClass != null && superClass.getInstanceType().isPropertyTypeDeclared(propertyName); // For interface boolean superInterfaceHasProperty = false; boolean superInterfaceHasDeclaredProperty = false; if (ctorType.isInterface()) { for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) { superInterfaceHasProperty = superInterfaceHasProperty || interfaceType.hasProperty(propertyName); superInterfaceHasDeclaredProperty = superInterfaceHasDeclaredProperty || interfaceType.isPropertyTypeDeclared(propertyName); } } boolean declaredOverride = info != null && info.isOverride(); boolean foundInterfaceProperty = false; if (ctorType.isConstructor()) { for (JSType implementedInterface : ctorType.getAllImplementedInterfaces()) { if (implementedInterface.isUnknownType() || implementedInterface.isEmptyType()) { continue; } FunctionType interfaceType = implementedInterface.toObjectType().getConstructor(); Preconditions.checkNotNull(interfaceType); boolean interfaceHasProperty = interfaceType.getPrototype().hasProperty(propertyName); foundInterfaceProperty = foundInterfaceProperty || interfaceHasProperty; if (reportMissingOverride.isOn() && !declaredOverride && interfaceHasProperty) { // @override not present, but the property does override an interface // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_INTERFACE

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>( t.makeError(n, UNKNOWN_OVERRIDE, propertyName, ctorType.getInstanceType().toString())); } } /** * Given a constructor or an interface type, find out whether the unknown * type is a supertype of the current type. */ private static boolean hasUnknownOrEmptySupertype(FunctionType ctor) { Preconditions.checkArgument(ctor.isConstructor() || ctor.isInterface()); Preconditions.checkArgument(!ctor.isUnknownType()); // The type system should notice inheritance cycles on its own // and break the cycle. while (true) { ObjectType maybeSuperInstanceType = ctor.getPrototype().getImplicitPrototype(); if (maybeSuperInstanceType == null) { return false; } if (maybeSuperInstanceType.isUnknownType() || maybeSuperInstanceType.isEmptyType()) { return true; } ctor = maybeSuperInstanceType.getConstructor(); if (ctor == null) { return false; } Preconditions.checkState(ctor.isConstructor() || ctor.isInterface()); } } /** * Visits an ASSIGN node for cases such as * <pre> * interface.property2.property = ...; * </pre> */ private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object, String property, Node lvalue, Node rvalue) { JSType rvalueType = getJSType(rvalue); // Only 2 values are allowed for methods: // goog.abstractMethod // function () {}; // or for properties, no assignment such as: // InterfaceFoo.prototype.foobar; String abstractMethodName = compiler.getCodingConvention().getAbstractMethodName(); if (!rvalueType.isFunctionType()) { // This is bad i18n style but we don't localize our compiler errors. String abstractMethodMessage = (abstractMethodName != null) ? ", or " + abstractMethodName : ""; compiler.report( t.makeError(object, INVALID_INTERFACE_MEMBER_DECLARATION, abstractMethodMessage)); } if (assign.getLastChild().isFunction() && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { compiler.report( t.makeError(object, INTERFACE_FUNCTION_NOT

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } // TODO(nicksantos): TypeCheck should never be attaching types to nodes. // All types should be attached by TypeInference. This is not true today // for legacy reasons. There are a number of places where TypeInference // doesn't attach a type, as a signal to TypeCheck that it needs to check // that node's type. /** * Ensure that the given node has a type. If it does not have one, * attach the UNKNOWN_TYPE. */ private void ensureTyped(NodeTraversal t, Node n) { ensureTyped(t, n, getNativeType(UNKNOWN_TYPE)); } private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) { ensureTyped(t, n, getNativeType(type)); } /** * Enforces type casts, and ensures the node is typed. * * A cast in the way that we use it in JSDoc annotations never * alters the generated code and therefore never can induce any runtime * operation. What this means is that a 'cast' is really just a compile * time constraint on the underlying value. In the future, we may add * support for run-time casts for compiled tests. * * To ensure some shred of sanity, we enforce the notion that the * type you are casting to may only meaningfully be a narrower type * than the underlying declared type. We also invalidate optimizations * on bad type casts. * * @param t The traversal object needed to report errors. * @param n The node getting a type assigned to it. * @param type The type to be assigned. */ private void ensureTyped(NodeTraversal t, Node n, JSType type) { // Make sure FUNCTION nodes always get function type. Preconditions.checkState(!n.isFunction() || type.isFunctionType() || type.isUnknownType()); JSDocInfo info = n.getJSDocInfo(); if (info != null) { if (info.hasType()) { JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry);

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> } @Override HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) { return self.getHotSwapPass(compiler); } }; } /** * Creates a new compiler pass to be run. */ final CompilerPass create(AbstractCompiler compiler) { Preconditions.checkState(!isCreated || !isOneTimePass, "One-time passes cannot be run multiple times: %s", name); isCreated = true; return createInternal(compiler); } /** * Creates a new compiler pass to be run. */ abstract protected CompilerPass createInternal(AbstractCompiler compiler); /** * Any factory whose CompilerPass has a corresponding hot-swap version should * override this. * * @param compiler The compiler that can has been used to do the full compile. */ HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) { // TODO(bashir): If in future most of PassFactory's in DefaultPassConfig // turns out to be DefaultPassConfig.HotSwapPassFactory, we should probably // change the implementation here by the one in HotSwapPassFactory. return null; } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> { ret = transform(n); ret.putBooleanProp(Node.QUOTED_PROP, true); } Preconditions.checkState(ret.isString()); return ret; } @Override Node processArrayLiteral(ArrayLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.ARRAYLIT); for (AstNode child : literalNode.getElements()) { Node c = transform(child); node.addChildToBack(c); } return node; } @Override Node processAssignment(Assignment assignmentNode) { Node assign = processInfixExpression(assignmentNode); Node target = assign.getFirstChild(); if (!validAssignmentTarget(target)) { errorReporter.error( "invalid assignment target", sourceName, target.getLineno(), "", 0); } return assign; } @Override Node processAstRoot(AstRoot rootNode) { Node node = newNode(Token.SCRIPT); for (com.google.javascript.rhino.head.Node child : rootNode) { node.addChildToBack(transform((AstNode)child)); } parseDirectives(node); return node; } /** * Parse the directives, encode them in the AST, and remove their nodes. * * For information on ES5 directives, see section 14.1 of * ECMA-262, Edition 5. * * It would be nice if Rhino would eventually take care of this for * us, but right now their directive-processing is a one-off. */ private void parseDirectives(Node node) { // Remove all the directives, and encode them in the AST. Set<String> directives = null; while (isDirective(node.getFirstChild())) { String directive = node.removeFirstChild().getFirstChild().getString(); if (directives == null) { directives = Sets.newHashSet(directive); } else { directives.add(directive); } } if (directives != null) { node.setDirectives(directives); } } private boolean isDirective(Node n) { if (n == null) return false; int n

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>isName()) { lp.addChildToBack(paramNode); } else { // We expect this in ideMode or when there is an error handling // destructuring parameter assignments which aren't supported // (an error has already been reported). Preconditions.checkState( config.isIdeMode || paramNode.isObjectLit() || paramNode.isArrayLit()); } } node.addChildToBack(lp); Node bodyNode = transform(functionNode.getBody()); if (!bodyNode.isBlock()) { // When in ideMode Rhino tries to parse some constructs the compiler // doesn't support, repair it here. see Rhino's // Parser#parseFunctionBodyExpr. Preconditions.checkState(config.isIdeMode); bodyNode = IR.block(); } parseDirectives(bodyNode); node.addChildToBack(bodyNode); return node; } @Override Node processIfStatement(IfStatement statementNode) { Node node = newNode(Token.IF); node.addChildToBack(transform(statementNode.getCondition())); node.addChildToBack(transformBlock(statementNode.getThenPart())); if (statementNode.getElsePart() != null) { node.addChildToBack(transformBlock(statementNode.getElsePart())); } return node; } @Override Node processInfixExpression(InfixExpression exprNode) { Node n = newNode( transformTokenType(exprNode.getType()), transform(exprNode.getLeft()), transform(exprNode.getRight())); n.setLineno(exprNode.getLineno()); n.setCharno(position2charno(exprNode.getAbsolutePosition())); maybeSetLengthFrom(n, exprNode); return n; } @Override Node processKeywordLiteral(KeywordLiteral literalNode) { return newNode(transformTokenType(literalNode.getType())); } @Override Node processLabel(Label labelNode) { return newStringNode(Token.LABEL_NAME, labelNode.getName()); } @Override Node processLabeledStatement(LabeledStatement statementNode) { Node node = newNode(Token.LABEL); Node prev = null; Node cur = node; for (Label label : statementNode.getLabels()) {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Token.GETTER_DEF); Preconditions.checkState(value.isFunction()); if (getFnParamNode(value).hasChildren()) { reportGetterParam(el.getLeft()); } } else if (el.isSetter()) { key.setType(Token.SETTER_DEF); Preconditions.checkState(value.isFunction()); if (!getFnParamNode(value).hasOneChild()) { reportSetterParam(el.getLeft()); } } key.addChildToFront(value); node.addChildToBack(key); } return node; } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ Node getFnParamNode(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.isFunction()); return fnNode.getFirstChild().getNext(); } @Override Node processObjectProperty(ObjectProperty propertyNode) { return processInfixExpression(propertyNode); } @Override Node processParenthesizedExpression(ParenthesizedExpression exprNode) { Node node = transform(exprNode.getExpression()); node.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE); return node; } @Override Node processPropertyGet(PropertyGet getNode) { Node leftChild = transform(getNode.getTarget()); Node newNode = newNode( Token.GETPROP, leftChild, transformAsString(getNode.getProperty())); newNode.setLineno(leftChild.getLineno()); newNode.setCharno(leftChild.getCharno()); maybeSetLengthFrom(newNode, getNode); return newNode; } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = newStringNode(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); maybeSetLengthFrom(literalStringNode, literalNode); Node node = newNode(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags != null && !flags.isEmpty()) { Node flagsNode = newStringNode(flags); // Assume the flags are on the same line as

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import static com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt.LINE; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.SourceExcerptProvider.ExcerptFormatter; import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt; /** * Lightweight message formatter. The format of messages this formatter * produces is very compact and to the point. * */ public class LightweightMessageFormatter extends AbstractMessageFormatter { private SourceExcerpt excerpt; private static final ExcerptFormatter excerptFormatter = new LineNumberingFormatter(); /** * A constructor for when the client doesn't care about source information. */ private LightweightMessageFormatter() { super(null); this.excerpt = LINE; } public LightweightMessageFormatter(SourceExcerptProvider source) { this(source, LINE); } public LightweightMessageFormatter(SourceExcerptProvider source, SourceExcerpt excerpt) { super(source); Preconditions.checkNotNull(source); this.excerpt = excerpt; } static LightweightMessageFormatter withoutSource() { return new LightweightMessageFormatter(); } @Override public String formatError(JSError error) { return format(error, false); } @Override public String formatWarning(JSError warning) { return format(warning, true); } private String format(J

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>().iterator().next()); } symbols.add(symbols.get(0)); return Joiner.on(" -> ").join(symbols); } public List<INPUT> getSortedList() { return Collections.<INPUT>unmodifiableList(sortedList); } /** * Gets all the dependencies of the given roots. The inputs must be returned * in a stable order. In other words, if A comes before B, and A does not * transitively depend on B, then A must also come before B in the returned * list. */ public List<INPUT> getSortedDependenciesOf(List<INPUT> roots) { return getDependenciesOf(roots, true); } /** * Gets all the dependencies of the given roots. The inputs must be returned * in a stable order. In other words, if A comes before B, and A does not * transitively depend on B, then A must also come before B in the returned * list. * * @param sorted If true, get them in topologically sorted order. If false, * get them in the original order they were passed to the compiler. */ public List<INPUT> getDependenciesOf(List<INPUT> roots, boolean sorted) { Preconditions.checkArgument(inputs.containsAll(roots)); Set<INPUT> included = Sets.newHashSet(); Deque<INPUT> worklist = new ArrayDeque<INPUT>(roots); while (!worklist.isEmpty()) { INPUT current = worklist.pop(); if (included.add(current)) { for (String req : current.getRequires()) { INPUT dep = provideMap.get(req); if (dep != null) { worklist.add(dep); } } } } ImmutableList.Builder<INPUT> builder = ImmutableList.builder(); for (INPUT current : (sorted ? sortedList : inputs)) { if (included.contains(current)) { builder.add(current); } } return builder.build(); } public List<INPUT> getInputsWithoutProvides() { return Collections.<INPUT>unmodifiableList(noProvides); } private static <T> List<T> topologicalStableSort( List<T> items, Multimap<T, T> deps) { if (items

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> this.filenamePrefix = filenamePrefix.endsWith(File.separator) ? filenamePrefix : filenamePrefix + File.separator; this.reportDependencies = reportDependencies; } @Override public void process(Node externs, Node root) { NodeTraversal .traverse(compiler, root, new ProcessCommonJsModulesCallback()); } String guessCJSModuleName(String filename) { return toModuleName(normalizeSourceName(filename)); } /** * For every file that is being processed this returns the module that * created for it. */ JSModule getModule() { return module; } /** * Turns a filename into a JS identifier that is used for moduleNames in * rewritten code. Removes leading ./, replaces / with $, removes trailing .js * and replaces - with _. All moduleNames get a "module$" prefix. */ public static String toModuleName(String filename) { return MODULE_NAME_PREFIX + filename.replaceAll("^\\." + Pattern.quote(File.separator), "") .replaceAll(Pattern.quote(File.separator), MODULE_NAME_SEPARATOR) .replaceAll("\\.js$", "").replaceAll("-", "_"); } /** * Turn a filename into a moduleName with support for relative addressing * with ./ and ../ based on currentFilename; */ public static String toModuleName(String requiredFilename, String currentFilename) { requiredFilename = requiredFilename.replaceAll("\\.js$", ""); currentFilename = currentFilename.replaceAll("\\.js$", ""); if (requiredFilename.startsWith("." + File.separator) || requiredFilename.startsWith(".." + File.separator)) { try { requiredFilename = (new URI(currentFilename)).resolve(new URI(requiredFilename)) .toString(); } catch (URISyntaxException e) { throw new RuntimeException(e); } } return toModuleName(requiredFilename); } private String normalizeSourceName(String filename) { if (filename.indexOf(filenamePrefix) == 0) { filename = filename.substring(filenamePrefix.length()); } return filename; } /** * Visits require, every "script" and special module.exports assignments. */ private class ProcessCommonJsModulesCallback extends AbstractPostOrderCallback { private int scriptNodeCount = 0;

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> private Set<String> modulesWithExports = Sets.newHashSet(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall() && n.getChildCount() == 2 && "require".equals(n.getFirstChild().getQualifiedName()) && n.getChildAtIndex(1).isString()) { visitRequireCall(t, n, parent); } if (n.isScript()) { scriptNodeCount++; visitScript(t, n); } if (n.isGetProp() && "module.exports".equals(n.getQualifiedName())) { visitModuleExports(n); } } /** * Visit require calls. Emit corresponding goog.require and rewrite require * to be a direct reference to name of require module. */ private void visitRequireCall(NodeTraversal t, Node require, Node parent) { String moduleName = toModuleName(require.getChildAtIndex(1).getString(), normalizeSourceName(t.getSourceName())); Node moduleRef = IR.name(moduleName).srcref(require); parent.replaceChild(require, moduleRef); Node script = getCurrentScriptNode(parent); if (reportDependencies) { t.getInput().addRequire(moduleName); } // Rewrite require("name"). script.addChildToFront(IR.exprResult( IR.call(IR.getprop(IR.name("goog"), IR.string("require")), IR.string(moduleName))).copyInformationFromForTree(require)); compiler.reportCodeChange(); } /** * Emit goog.provide and add suffix to all global vars to avoid conflicts * with other modules. */ private void visitScript(NodeTraversal t, Node script) { Preconditions.checkArgument(scriptNodeCount == 1, "ProcessCommonJSModules supports only one invocation per " + "CompilerInput / script node"); String moduleName = guessCJSModuleName(normalizeSourceName(script .getSourceFileName())); script.addChildToFront(IR.var(IR.name(moduleName), IR.objectlit()) .copyInformationFromForTree(script)); if (reportDependencies) { CompilerInput ci = t.getInput(); ci.addProvide(moduleName); JSModule m = new JSModule(moduleName); m.add

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> having a mismatch * in super class declaration, and only before properties on that type are * processed. */ final void setImplicitPrototype(ObjectType implicitPrototype) { checkState(!hasCachedValues()); this.implicitPrototypeFallback = implicitPrototype; } @Override public String getReferenceName() { if (className != null) { return className; } else if (ownerFunction != null) { return ownerFunction.getReferenceName() + ".prototype"; } else { return null; } } @Override public boolean hasReferenceName() { return className != null || ownerFunction != null; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } // Union types if (that.isUnionType()) { // The static {@code JSType.isSubtype} check already decomposed // union types, so we don't need to check those again. return false; } // record types if (that.isRecordType()) { return RecordType.isSubtype(this, that.toMaybeRecordType()); } // Interfaces // Find all the interfaces implemented by this class and compare each one // to the interface instance. ObjectType thatObj = that.toObjectType(); ObjectType thatCtor = thatObj == null ? null : thatObj.getConstructor(); if (thatCtor != null && thatCtor.isInterface()) { Iterable<ObjectType> thisInterfaces = getCtorImplementedInterfaces(); for (ObjectType thisInterface : thisInterfaces) { if (thisInterface.isSubtype(that)) { return true; } } } if (getConstructor() != null && getConstructor().isInterface()) { for (ObjectType thisInterface : getCtorExtendedInterfaces()) { if (thisInterface.isSubtype(that)) { return true; } } } // other prototype based objects if (isUnknownType() || implicitPrototypeChainIsUnknown()) { // If unsure, say 'yes', to avoid spurious warnings. // TODO(user): resolve the prototype chain completely in all cases, // to avoid guessing. return true; } return this.isImplicitPrototype(thatObj); } private boolean implicitPrototypeChainIsUnknown() {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> ObjectType p = getImplicitPrototype(); while (p != null) { if (p.isUnknownType()) { return true; } p = p.getImplicitPrototype(); } return false; } @Override public boolean hasCachedValues() { return super.hasCachedValues(); } /** Whether this is a built-in object. */ @Override public boolean isNativeObjectType() { return nativeType; } @Override void setOwnerFunction(FunctionType type) { Preconditions.checkState(ownerFunction == null || type == null); ownerFunction = type; } @Override public FunctionType getOwnerFunction() { return ownerFunction; } @Override public Iterable<ObjectType> getCtorImplementedInterfaces() { return isFunctionPrototypeType() ? getOwnerFunction().getImplementedInterfaces() : ImmutableList.<ObjectType>of(); } @Override public Iterable<ObjectType> getCtorExtendedInterfaces() { return isFunctionPrototypeType() ? getOwnerFunction().getExtendedInterfaces() : ImmutableList.<ObjectType>of(); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setResolvedTypeInternal(this); ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype != null) { implicitPrototypeFallback = (ObjectType) implicitPrototype.resolve(t, scope); } for (Property prop : properties.values()) { prop.setType(safeResolve(prop.getType(), t, scope)); } return this; } @Override public void matchConstraint(ObjectType constraintObj) { // We only want to match constraints on anonymous types. if (hasReferenceName()) { return; } // Handle the case where the constraint object is a record type. // // param constraintObj {{prop: (number|undefined)}} // function f(constraintObj) {} // f({}); // // We want to modify the object literal to match the constraint, by // taking any each property on the record and trying to match // properties on this object. if (constraintObj.isRecordType()) { for (String prop : constraintObj.getOwnPropertyNames()) { JSType propType = constraintObj.getPropertyType(prop); if (!isPropertyTypeDeclared(prop)) {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>_LOOP_ERROR", "Exceeded max number of optimization iterations: {0}"); public static final DiagnosticType MOTION_ITERATIONS_ERROR = DiagnosticType.error("JSC_OPTIMIZE_LOOP_ERROR", "Exceeded max number of code motion iterations: {0}"); private static final long COMPILER_STACK_SIZE = 1048576L; /** * Logger for the whole com.google.javascript.jscomp domain - * setting configuration for this logger affects all loggers * in other classes within the compiler. */ private static final Logger logger = Logger.getLogger("com.google.javascript.jscomp"); private final PrintStream outStream; private GlobalVarReferenceMap globalRefMap = null; private volatile double progress = 0.0; /** * Creates a Compiler that reports errors and warnings to its logger. */ public Compiler() { this((PrintStream) null); } /** * Creates n Compiler that reports errors and warnings to an output * stream. */ public Compiler(PrintStream stream) { addChangeHandler(recentChange); outStream = stream; } /** * Creates a Compiler that uses a custom error manager. */ public Compiler(ErrorManager errorManager) { this(); setErrorManager(errorManager); } /** * Sets the error manager. * * @param errorManager the error manager, it cannot be {@code null} */ public void setErrorManager(ErrorManager errorManager) { Preconditions.checkNotNull( errorManager, "the error manager cannot be null"); this.errorManager = errorManager; } /** * Creates a message formatter instance corresponding to the value of * {@link CompilerOptions}. */ private MessageFormatter createMessageFormatter() { boolean colorize = options.shouldColorizeErrorOutput(); return options.errorFormat.toFormatter(this, colorize); } /** * Initialize the compiler options. Only necessary if you're not doing * a normal compile() job. */ public void initOptions(CompilerOptions options) { this.options = options; if (errorManager == null) { if (outStream == null) { setErrorManager( new LoggerErrorManager(createMessageFormatter(), logger)); } else { Print

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>File extern, SourceFile input, CompilerOptions options) { return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options); } public Result compile( SourceFile extern, JSSourceFile[] input, CompilerOptions options) { return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options); } public Result compile( JSSourceFile extern, JSModule[] modules, CompilerOptions options) { return compileModules( Lists.newArrayList(extern), Lists.newArrayList(modules), options); } /** * Compiles a list of inputs. */ public Result compile(JSSourceFile[] externs, JSSourceFile[] inputs, CompilerOptions options) { return compile(Lists.<SourceFile>newArrayList(externs), Lists.<SourceFile>newArrayList(inputs), options); } /** * Compiles a list of inputs. */ public <T1 extends SourceFile, T2 extends SourceFile> Result compile( List<T1> externs, List<T2> inputs, CompilerOptions options) { // The compile method should only be called once. Preconditions.checkState(jsRoot == null); try { init(externs, inputs, options); if (hasErrors()) { return getResult(); } return compile(); } finally { Tracer t = newTracer("generateReport"); errorManager.generateReport(); stopTracer(t, "generateReport"); } } /** * Compiles a list of modules. */ public Result compile(JSSourceFile[] externs, JSModule[] modules, CompilerOptions options) { return compileModules(Lists.<SourceFile>newArrayList(externs), Lists.<JSModule>newArrayList(modules), options); } /** * Compiles a list of modules. */ public <T extends SourceFile> Result compileModules(List<T> externs, List<JSModule> modules, CompilerOptions options) { // The compile method should only be called once. Preconditions.checkState(jsRoot == null); try { initModules(externs, modules, options); if (hasErrors()) { return getResult(); } return compile(); } finally { Tracer t = newTracer("generateReport"); error

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> // own PassConfig object. Preconditions.checkNotNull(passes); if (this.passes != null) { throw new IllegalStateException("this.passes has already been assigned"); } this.passes = passes; } /** * Carry out any special checks or procedures that need to be done before * proceeding with rest of the compilation process. * * @return true, to continue with compilation */ boolean precheck() { return true; } public void check() { runCustomPasses(CustomPassExecutionTime.BEFORE_CHECKS); // We are currently only interested in check-passes for progress reporting // as it is used for IDEs, that's why the maximum progress is set to 1.0. PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker, new PhaseOptimizer.ProgressRange(getProgress(), 1.0)); if (options.devMode == DevMode.EVERY_PASS) { phaseOptimizer.setSanityCheck(sanityCheck); } phaseOptimizer.consume(getPassConfig().getChecks()); phaseOptimizer.process(externsRoot, jsRoot); if (hasErrors()) { return; } // TODO(nicksantos): clean this up. The flow here is too hard to follow. if (options.nameAnonymousFunctionsOnly) { return; } if (options.removeTryCatchFinally) { removeTryCatchFinally(); } if (options.getTweakProcessing().shouldStrip() || !options.stripTypes.isEmpty() || !options.stripNameSuffixes.isEmpty() || !options.stripTypePrefixes.isEmpty() || !options.stripNamePrefixes.isEmpty()) { stripCode(options.stripTypes, options.stripNameSuffixes, options.stripTypePrefixes, options.stripNamePrefixes); } runCustomPasses(CustomPassExecutionTime.BEFORE_OPTIMIZATIONS); } private void externExports() { logger.fine("Creating extern file for exports"); startPass("externExports"); ExternExportsPass pass = new ExternExportsPass(this); process(pass); externExports = pass.getGeneratedExterns(); endPass(); } @Override void process(CompilerPass p) { p.process(externsRoot,

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> jsRoot); } private final PassFactory sanityCheck = new PassFactory("sanityCheck", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new SanityCheck(compiler); } }; private void maybeSanityCheck() { if (options.devMode == DevMode.EVERY_PASS) { runSanityCheck(); } } private void runSanityCheck() { sanityCheck.create(this).process(externsRoot, jsRoot); } /** * Removes try/catch/finally statements for easier debugging. */ void removeTryCatchFinally() { logger.fine("Remove try/catch/finally"); startPass("removeTryCatchFinally"); RemoveTryCatch r = new RemoveTryCatch(this); process(r); endPass(); } /** * Strips code for smaller compiled code. This is useful for removing debug * statements to prevent leaking them publicly. */ void stripCode(Set<String> stripTypes, Set<String> stripNameSuffixes, Set<String> stripTypePrefixes, Set<String> stripNamePrefixes) { logger.fine("Strip code"); startPass("stripCode"); StripCode r = new StripCode(this, stripTypes, stripNameSuffixes, stripTypePrefixes, stripNamePrefixes); if (options.getTweakProcessing().shouldStrip()) { r.enableTweakStripping(); } process(r); endPass(); } /** * Runs custom passes that are designated to run at a particular time. */ private void runCustomPasses(CustomPassExecutionTime executionTime) { if (options.customPasses != null) { Tracer t = newTracer("runCustomPasses"); try { for (CompilerPass p : options.customPasses.get(executionTime)) { process(p); } } finally { stopTracer(t, "runCustomPasses"); } } } private Tracer currentTracer = null; private String currentPassName = null; /** * Marks the beginning of a pass. */ void startPass(String passName) { Preconditions.checkState(currentTracer == null); currentPassName = passName; current

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Tracer = newTracer(passName); } /** * Marks the end of a pass. */ void endPass() { Preconditions.checkState(currentTracer != null, "Tracer should not be null at the end of a pass."); stopTracer(currentTracer, currentPassName); String passToCheck = currentPassName; currentPassName = null; currentTracer = null; maybeSanityCheck(); } /** * Returns a new tracer for the given pass name. */ Tracer newTracer(String passName) { String comment = passName + (recentChange.hasCodeChanged() ? " on recently changed AST" : ""); if (options.tracer.isOn()) { tracker.recordPassStart(passName); } return new Tracer("Compiler", comment); } void stopTracer(Tracer t, String passName) { long result = t.stop(); if (options.tracer.isOn()) { tracker.recordPassStop(passName, result); } } /** * Returns the result of the compilation. */ public Result getResult() { PassConfig.State state = getPassConfig().getIntermediateState(); return new Result(getErrors(), getWarnings(), debugLog.toString(), state.variableMap, state.propertyMap, state.anonymousFunctionNameMap, state.stringMap, functionInformationMap, sourceMap, externExports, state.cssNames, state.idGeneratorMap); } /** * Returns an array constructed from errors + temporary warnings. */ public JSError[] getMessages() { return getErrors(); } /** * Returns the array of errors (never null). */ public JSError[] getErrors() { return errorManager.getErrors(); } /** * Returns the array of warnings (never null). */ public JSError[] getWarnings() { return errorManager.getWarnings(); } @Override public Node getRoot() { return externAndJsRoot; } /** * Creates a new id for making unique names. */ private int nextUniqueNameId() { return uniqueNameId++; } /** * Resets the unique name id counter */ @VisibleForTesting void resetUniqueNameId() { uniqueNameId

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> = 0; } @Override Supplier<String> getUniqueNameIdSupplier() { final Compiler self = this; return new Supplier<String>() { @Override public String get() { return String.valueOf(self.nextUniqueNameId()); } }; } @Override boolean areNodesEqualForInlining(Node n1, Node n2) { if (options.ambiguateProperties || options.disambiguateProperties) { // The type based optimizations require that type information is preserved // during other optimizations. return n1.isEquivalentToTyped(n2); } else { return n1.isEquivalentTo(n2); } } //------------------------------------------------------------------------ // Inputs //------------------------------------------------------------------------ // TODO(nicksantos): Decide which parts of these belong in an AbstractCompiler // interface, and which ones should always be injected. @Override public CompilerInput getInput(InputId id) { return inputsById.get(id); } /** * Removes an input file from AST. * @param id The id of the input to be removed. */ protected void removeExternInput(InputId id) { CompilerInput input = getInput(id); if (input == null) { return; } Preconditions.checkState(input.isExtern(), "Not an extern input: %s", input.getName()); inputsById.remove(id); externs.remove(input); Node root = input.getAstRoot(this); if (root != null) { root.detachFromParent(); } } @Override public CompilerInput newExternInput(String name) { SourceAst ast = new SyntheticAst(name); if (inputsById.containsKey(ast.getInputId())) { throw new IllegalArgumentException("Conflicting externs name: " + name); } CompilerInput input = new CompilerInput(ast, true); putCompilerInput(input.getInputId(), input); externsRoot.addChildToFront(ast.getAstRoot(this)); externs.add(0, input); return input; } private CompilerInput putCompilerInput(InputId id, CompilerInput input) { input.setCompiler(this); return inputsById.put(id

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, input); } /** Add a source input dynamically. Intended for incremental compilation. */ void addIncrementalSourceAst(JsAst ast) { InputId id = ast.getInputId(); Preconditions.checkState(getInput(id) == null, "Duplicate input %s", id.getIdName()); putCompilerInput(id, new CompilerInput(ast)); } /** * Replace a source input dynamically. Intended for incremental * re-compilation. * * If the new source input doesn't parse, then keep the old input * in the AST and return false. * * @return Whether the new AST was attached successfully. */ boolean replaceIncrementalSourceAst(JsAst ast) { CompilerInput oldInput = getInput(ast.getInputId()); Preconditions.checkNotNull(oldInput, "No input to replace: %s", ast.getInputId().getIdName()); Node newRoot = ast.getAstRoot(this); if (newRoot == null) { return false; } Node oldRoot = oldInput.getAstRoot(this); if (oldRoot != null) { oldRoot.getParent().replaceChild(oldRoot, newRoot); } else { getRoot().getLastChild().addChildToBack(newRoot); } CompilerInput newInput = new CompilerInput(ast); putCompilerInput(ast.getInputId(), newInput); JSModule module = oldInput.getModule(); if (module != null) { module.addAfter(newInput, oldInput); module.remove(oldInput); } // Verify the input id is set properly. Preconditions.checkState( newInput.getInputId().equals(oldInput.getInputId())); InputId inputIdOnAst = newInput.getAstRoot(this).getInputId(); Preconditions.checkState(newInput.getInputId().equals(inputIdOnAst)); inputs.remove(oldInput); return true; } /** * Add a new source input dynamically. Intended for incremental compilation. * <p> * If the new source input doesn't parse, it will not be added, and a false * will be returned. * * @param ast the JS Source to add. * @return true if the source was added successfully, false otherwise. * @

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>throws IllegalStateException if an input for this ast already exists. */ boolean addNewSourceAst(JsAst ast) { CompilerInput oldInput = getInput(ast.getInputId()); if (oldInput != null) { throw new IllegalStateException( "Input already exists: " + ast.getInputId().getIdName()); } Node newRoot = ast.getAstRoot(this); if (newRoot == null) { return false; } getRoot().getLastChild().addChildToBack(newRoot); CompilerInput newInput = new CompilerInput(ast); // TODO(tylerg): handle this for multiple modules at some point. if (moduleGraph == null && !modules.isEmpty()) { // singleton module modules.get(0).add(newInput); } putCompilerInput(ast.getInputId(), newInput); return true; } @Override JSModuleGraph getModuleGraph() { return moduleGraph; } /** * Gets a module graph. This will always return a module graph, even * in the degenerate case when there's only one module. */ JSModuleGraph getDegenerateModuleGraph() { return moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph; } @Override public JSTypeRegistry getTypeRegistry() { if (typeRegistry == null) { typeRegistry = new JSTypeRegistry(oldErrorReporter, options.looseTypes); } return typeRegistry; } @Override public MemoizedScopeCreator getTypedScopeCreator() { return getPassConfig().getTypedScopeCreator(); } @SuppressWarnings("unchecked") DefaultPassConfig ensureDefaultPassConfig() { PassConfig passes = getPassConfig().getBasePassConfig(); Preconditions.checkState(passes instanceof DefaultPassConfig, "PassConfigs must eventually delegate to the DefaultPassConfig"); return (DefaultPassConfig) passes; } public SymbolTable buildKnownSymbolTable() { SymbolTable symbolTable = new SymbolTable(getTypeRegistry()); MemoizedScopeCreator typedScopeCreator = getTypedScopeCreator(); if (typedScopeCreator != null) { symbolTable.addScopes(typedScopeCreator.getAllMemoizedScopes()); symbolTable.addSymbolsFrom(typedScopeCreator); } else { symbolTable.findScopes(this, extern

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) { return ""; } CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module: " + module.getName()); } toSource(cb, i, scriptNode); } return cb.toString(); } }); } /** * Converts the parse tree for each input in a module back to JS code. */ public String[] toSourceArray(final JSModule module) { return runInCompilerThread(new Callable<String[]>() { @Override public String[] call() throws Exception { List<CompilerInput> inputs = module.getInputs(); int numInputs = inputs.size(); if (numInputs == 0) { return new String[0]; } String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module input: " + inputs.get(i).getName()); } cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } }); } /** * Writes out JS code from a root node. If printing input delimiters, this * method will attach a comment to the start of the text indicating which * input the output derived from. If there were any preserve annotations * within the root's source, they will also be printed in a block comment * at the beginning of the output. */ public void toSource(final CodeBuilder cb, final int inputSeqNum, final Node root) { runInCompilerThread(new Callable<Void>() { @Override public Void call() throws Exception { if (options.printInputDelimiter) { if ((cb.getLength() > 0) && !cb.endsWith("\n")) { cb.append("\n"); // Make sure that the label starts on a new line } Preconditions

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.checkState(root.isScript()); String delimiter = options.inputDelimiter; String inputName = root.getInputId().getIdName(); String sourceName = root.getSourceFileName(); Preconditions.checkState(sourceName != null); Preconditions.checkState(!sourceName.isEmpty()); delimiter = delimiter .replaceAll("%name%", Matcher.quoteReplacement(inputName)) .replaceAll("%num%", String.valueOf(inputSeqNum)); cb.append(delimiter) .append("\n"); } if (root.getJSDocInfo() != null && root.getJSDocInfo().getLicense() != null) { cb.append("/*\n") .append(root.getJSDocInfo().getLicense()) .append("*/\n"); } // If there is a valid source map, then indicate to it that the current // root node's mappings are offset by the given string builder buffer. if (options.sourceMapOutputPath != null) { sourceMap.setStartingPosition( cb.getLineIndex(), cb.getColumnIndex()); } // if LanguageMode is ECMASCRIPT5_STRICT, only print 'use strict' // for the first input file String code = toSource(root, sourceMap, inputSeqNum == 0); if (!code.isEmpty()) { cb.append(code); // In order to avoid parse ambiguity when files are concatenated // together, all files should end in a semi-colon. Do a quick // heuristic check if there's an obvious semi-colon already there. int length = code.length(); char lastChar = code.charAt(length - 1); char secondLastChar = length >= 2 ? code.charAt(length - 2) : '\0'; boolean hasSemiColon = lastChar == ';' || (lastChar == '\n' && secondLastChar == ';'); if (!hasSemiColon) { cb.append(";"); } } return null; } }); } /** * Generates JavaScript source code for an AST, doesn't generate source * map info. */ @Override String toSource(Node n) { initCompilerOptionsIfTesting(); return toSource(n, null, true); } /** *

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> @Override public boolean acceptEcmaScript5() { switch (options.getLanguageIn()) { case ECMASCRIPT5: case ECMASCRIPT5_STRICT: return true; } return false; } public LanguageMode languageMode() { return options.getLanguageIn(); } @Override public boolean acceptConstKeyword() { return options.acceptConstKeyword; } @Override Config getParserConfig() { if (parserConfig == null) { Config.LanguageMode mode; switch (options.getLanguageIn()) { case ECMASCRIPT3: mode = Config.LanguageMode.ECMASCRIPT3; break; case ECMASCRIPT5: mode = Config.LanguageMode.ECMASCRIPT5; break; case ECMASCRIPT5_STRICT: mode = Config.LanguageMode.ECMASCRIPT5_STRICT; break; default: throw new IllegalStateException("unexpected language mode"); } parserConfig = ParserRunner.createConfig( isIdeMode(), mode, acceptConstKeyword(), options.extraAnnotationNames); } return parserConfig; } @Override public boolean isTypeCheckingEnabled() { return options.checkTypes; } //------------------------------------------------------------------------ // Error reporting //------------------------------------------------------------------------ /** * The warning classes that are available from the command-line, and * are suppressible by the {@code @suppress} annotation. */ protected DiagnosticGroups getDiagnosticGroups() { return new DiagnosticGroups(); } @Override public void report(JSError error) { CheckLevel level = error.getDefaultLevel(); if (warningsGuard != null) { CheckLevel newLevel = warningsGuard.level(error); if (newLevel != null) { level = newLevel; } } if (level.isOn()) { if (getOptions().errorHandler != null) { getOptions().errorHandler.report(level, error); } errorManager.report(level, error); } } @Override public CheckLevel getErrorLevel(JSError error) { Preconditions.checkNotNull(options); return warningsGuard.level(error); } /** * Report an internal error. */ @Override void throwInternalError(

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>modules; passes = createPassConfigInternal(); getPassConfig().setIntermediateState(state.passConfigState); typeRegistry = state.typeRegistry; setLifeCycleStage(state.lifeCycleStage); injectedLibraries.clear(); injectedLibraries.putAll(state.injectedLibraries); } @VisibleForTesting List<CompilerInput> getInputsForTesting() { return inputs; } @VisibleForTesting List<CompilerInput> getExternsForTesting() { return externs; } @Override boolean hasRegExpGlobalReferences() { return hasRegExpGlobalReferences; } @Override void setHasRegExpGlobalReferences(boolean references) { hasRegExpGlobalReferences = references; } @Override void updateGlobalVarReferences(Map<Var, ReferenceCollection> refMapPatch, Node collectionRoot) { Preconditions.checkState(collectionRoot.isScript() || collectionRoot.isBlock()); if (globalRefMap == null) { globalRefMap = new GlobalVarReferenceMap(getInputsInOrder(), getExternsInOrder()); } globalRefMap.updateGlobalVarReferences(refMapPatch, collectionRoot); } @Override GlobalVarReferenceMap getGlobalVarReferences() { return globalRefMap; } @Override CompilerInput getSynthesizedExternsInput() { if (synthesizedExternsInput == null) { synthesizedExternsInput = newExternInput(SYNTHETIC_EXTERNS); } return synthesizedExternsInput; } @Override public double getProgress() { return progress; } @Override void setProgress(double newProgress) { if (newProgress > 1.0) { progress = 1.0; } else if (newProgress < 0.0) { progress = 0.0; } else { progress = newProgress; } } /** * Replaces one file in a hot-swap mode. The given JsAst should be made * from a new version of a file that already was present in the last compile * call. If the file is new, this will silently ignored. * * @param ast the ast of the file that is being replaced */ public void replaceScript(JsAst

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> ast) { CompilerInput input = this.getInput(ast.getInputId()); if (!replaceIncrementalSourceAst(ast)) { return; } Node originalRoot = input.getAstRoot(this); processNewScript(ast, originalRoot); } /** * Adds a new Script AST to the compile state. If a script for the same file * already exists the script will not be added, instead a call to * #replaceScript should be used. * * @param ast the ast of the new file */ public void addNewScript(JsAst ast) { if (!addNewSourceAst(ast)) { return; } Node emptyScript = new Node(Token.SCRIPT); InputId inputId = ast.getInputId(); emptyScript.setInputId(inputId); emptyScript.setStaticSourceFile( SourceFile.fromCode(inputId.getIdName(), "")); processNewScript(ast, emptyScript); } private void processNewScript(JsAst ast, Node originalRoot) { Node js = ast.getAstRoot(this); Preconditions.checkNotNull(js); runHotSwap(originalRoot, js, this.getCleanupPassConfig()); // NOTE: If hot swap passes that use GlobalNamespace are added, we will need // to revisit this approach to clearing GlobalNamespaces runHotSwapPass(null, null, ensureDefaultPassConfig().garbageCollectChecks); this.getTypeRegistry().clearNamedTypes(); this.removeSyntheticVarsInput(); runHotSwap(originalRoot, js, this.ensureDefaultPassConfig()); } /** * Execute the passes from a PassConfig instance over a single replaced file. */ private void runHotSwap( Node originalRoot, Node js, PassConfig passConfig) { for (PassFactory passFactory : passConfig.getChecks()) { runHotSwapPass(originalRoot, js, passFactory); } } private void runHotSwapPass( Node originalRoot, Node js, PassFactory passFactory) { HotSwapCompilerPass pass = passFactory.getHotSwapPass(this); if (pass != null) { logger.info("Performing HotSwap for pass " + passFactory.getName()); pass.hotSwapScript(js, originalRoot); } } private PassConfig

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> } @Override public SourceFile getSourceFile() { return sourceFile; } @Override public void setSourceFile(SourceFile file) { Preconditions.checkState(fileName.equals(file.getName())); sourceFile = file; } private void parse(AbstractCompiler compiler) { try { logger_.fine("Parsing: " + sourceFile.getName()); root = ParserRunner.parse(sourceFile, sourceFile.getCode(), compiler.getParserConfig(), compiler.getDefaultErrorReporter(), logger_); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName())); } if (root == null || compiler.hasHaltingErrors()) { // There was a parse error or IOException, so use a dummy block. root = IR.script(); } else { compiler.prepareAst(root); } // Set the source name so that the compiler passes can track // the source file and module. root.setStaticSourceFile(sourceFile); } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Name, int lineno, int charno) { super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); Preconditions.checkNotNull(reference); this.reference = reference; this.sourceName = sourceName; this.lineno = lineno; this.charno = charno; } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { if (!isResolved()) { // If this is an unresolved object type, we need to save all its // properties and define them when it is resolved. if (propertyContinuations == null) { propertyContinuations = Lists.newArrayList(); } propertyContinuations.add( new PropertyContinuation( propertyName, type, inferred, propertyNode)); return true; } else { return super.defineProperty( propertyName, type, inferred, propertyNode); } } private void finishPropertyContinuations() { ObjectType referencedObjType = getReferencedObjTypeInternal(); if (referencedObjType != null && !referencedObjType.isUnknownType()) { if (propertyContinuations != null) { for (PropertyContinuation c : propertyContinuations) { c.commit(this); } } } propertyContinuations = null; } /** Returns the type to which this refers (which is unknown if unresolved). */ public JSType getReferencedType() { return getReferencedTypeInternal(); } @Override public String getReferenceName() { return reference; } @Override String toStringHelper(boolean forAnnotations) { return reference; } @Override public boolean hasReferenceName() { return true; } @Override boolean isNamedType() { return true; } @Override public boolean isNominalType() { return true; } /** * Two named types are equivalent if they are the same {@code * ObjectType} object. This is complicated by the fact that isEquivalent * is sometimes called before we have a chance to resolve the type * names. * * @return {@code true} iff {@code that} == {@code this} or {@code that} * is a {@link NamedType} whose reference is the same as ours, * or {@code that} is the type we reference

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Compiler compiler, RedeclarationHandler redeclarationHandler) { this.compiler = compiler; this.redeclarationHandler = redeclarationHandler; } @Override public Scope createScope(Node n, Scope parent) { inputId = null; if (parent == null) { scope = new Scope(n, compiler); } else { scope = new Scope(parent, n); } scanRoot(n, parent); inputId = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) { if (n.isFunction()) { if (inputId == null) { inputId = NodeUtil.getInputId(n); // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached // from the AST. // Is it meaningful to build a scope for detached FUNCTION node? } final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnNameNode); } // Args: Declare function variables Preconditions.checkState(args.isParamList()); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.isName()); declareVar(a); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); declareVar(child); child = next; }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().isName()); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: inputId = n.getInputId(); Preconditions.checkNotNull(inputId); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, CompilerInput input); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Node parent = n.getParent(); // Don't allow multiple variables to be declared at the top-level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.isCatch() && parent.isCatch()) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar); if (!

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>allowDupe) { compiler.report( JSError.make(NodeUtil.getSourceName(n), n, VAR_MULTIPLY_DECLARED_ERROR, name, (origVar.input != null ? origVar.input.getName() : "??"))); } } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) { // Disallow shadowing "arguments" as we can't handle with our current // scope modeling. compiler.report( JSError.make(NodeUtil.getSourceName(n), n, VAR_ARGUMENTS_SHADOWED_ERROR)); } } } /** * Declares a variable. * * @param n The node corresponding to the variable name. */ private void declareVar(Node n) { Preconditions.checkState(n.isName()); CompilerInput input = compiler.getInput(inputId); String name = n.getString(); if (scope.isDeclared(name, false) || (scope.isLocal() && name.equals(ARGUMENTS))) { redeclarationHandler.onRedeclaration( scope, name, n, input); } else { scope.declare(name, n, null, input); } } /** * @param n The name node to check. * @param origVar The associated Var. * @return Whether duplicated declarations warnings should be suppressed * for the given node. */ static boolean hasDuplicateDeclarationSuppression(Node n, Scope.Var origVar) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); Node origParent = origVar.getParentNode(); JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } if (info != null && info.getSuppressions().contains("duplicate")) { return true; } info = origVar.nameNode.getJSDocInfo(); if (info == null) { info = origParent.getJSDocInfo(); } return (info != null && info.getSuppressions().contains("duplicate")); } /** * Generates an untyped global scope from the root of

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> output lattice element type. */ static class FlowState<L extends LatticeElement> implements Annotation { private L in; private L out; /** * Private constructor. No other classes should create new states. * * @param inState Input. * @param outState Output. */ private FlowState(L inState, L outState) { Preconditions.checkNotNull(inState); Preconditions.checkNotNull(outState); this.in = inState; this.out = outState; } L getIn() { return in; } void setIn(L in) { Preconditions.checkNotNull(in); this.in = in; } L getOut() { return out; } void setOut(L out) { Preconditions.checkNotNull(out); this.out = out; } @Override public String toString() { return String.format("IN: %s OUT: %s", in, out); } @Override public int hashCode() { return Objects.hashCode(in, out); } } /** * The exception to be thrown if the analysis has been running for a long * number of iterations. Chances are the analysis is not monotonic, a * fixed-point cannot be found and it is currently stuck in an infinite loop. */ static class MaxIterationsExceededException extends RuntimeException { private static final long serialVersionUID = 1L; MaxIterationsExceededException(String msg) { super(msg); } } abstract static class BranchedForwardDataFlowAnalysis <N, L extends LatticeElement> extends DataFlowAnalysis<N, L> { @Override protected void initialize() { orderedWorkSet.clear(); for (DiGraphNode<N, Branch> node : getCfg().getDirectedGraphNodes()) { int outEdgeCount = getCfg().getOutEdges(node.getValue()).size(); List<L> outLattices = Lists.newArrayList(); for (int i = 0; i < outEdgeCount; i++) { outLattices.add(createInitialEstimateLattice()); } node.setAnnotation(new BranchedFlowState<L>( createInitialEstimateLattice(), outLattices)); if (node != getCfg().

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>getImplicitReturn()) { orderedWorkSet.add(node); } } } BranchedForwardDataFlowAnalysis(ControlFlowGraph<N> targetCfg, JoinOp<L> joinOp) { super(targetCfg, joinOp); } /** * Returns the lattice element at the exit point. Needs to be overridden * because we use a BranchedFlowState instead of a FlowState; ugh. */ @Override L getExitLatticeElement() { DiGraphNode<N, Branch> node = getCfg().getImplicitReturn(); BranchedFlowState<L> state = node.getAnnotation(); return state.getIn(); } @Override final boolean isForward() { return true; } /** * The branched flow function maps a single lattice to a list of output * lattices. * * <p>Each outgoing edge of a node will have a corresponding output lattice * in the ordered returned by * {@link com.google.javascript.jscomp.graph.DiGraph#getOutEdges(Object)} * in the returned list. * * @return A list of output values depending on the edge's branch type. */ abstract List<L> branchedFlowThrough(N node, L input); @Override protected final boolean flow(DiGraphNode<N, Branch> node) { BranchedFlowState<L> state = node.getAnnotation(); List<L> outBefore = state.out; state.out = branchedFlowThrough(node.getValue(), state.in); Preconditions.checkState(outBefore.size() == state.out.size()); for (int i = 0; i < outBefore.size(); i++) { if (!outBefore.get(i).equals(state.out.get(i))) { return true; } } return false; } @Override protected void joinInputs(DiGraphNode<N, Branch> node) { BranchedFlowState<L> state = node.getAnnotation(); List<DiGraphNode<N, Branch>> predNodes = getCfg().getDirectedPredNodes(node); List<L> values = new ArrayList<L>(predNodes.size()); for (DiGraph

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Node<N, Branch> predNode : predNodes) { BranchedFlowState<L> predNodeState = predNode.getAnnotation(); L in = predNodeState.out.get( getCfg().getDirectedSuccNodes(predNode).indexOf(node)); values.add(in); } if (getCfg().getEntry() == node) { state.setIn(createEntryLattice()); } else if (!values.isEmpty()) { state.setIn(joinOp.apply(values)); } } } /** * The in and out states of a node. * * @param <L> Input and output lattice element type. */ static class BranchedFlowState<L extends LatticeElement> implements Annotation { private L in; private List<L> out; /** * Private constructor. No other classes should create new states. * * @param inState Input. * @param outState Output. */ private BranchedFlowState(L inState, List<L> outState) { Preconditions.checkNotNull(inState); Preconditions.checkNotNull(outState); this.in = inState; this.out = outState; } L getIn() { return in; } void setIn(L in) { Preconditions.checkNotNull(in); this.in = in; } List<L> getOut() { return out; } void setOut(List<L> out) { Preconditions.checkNotNull(out); for (L item : out) { Preconditions.checkNotNull(item); } this.out = out; } @Override public String toString() { return String.format("IN: %s OUT: %s", in, out); } @Override public int hashCode() { return Objects.hashCode(in, out); } } /** * Compute set of escaped variables. When a variable is escaped in a * dataflow analysis, it can be reference outside of the code that we are * analyzing. A variable is escaped if any of the following is true: * * <p><ol> * <li>It is defined as the exception name in CATCH clause so it became a * variable local not

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>PropNode} does not * correspond to a valid JS message node */ private void extractMessageFromProperty( Builder builder, Node getPropNode, Node assignNode) throws MalformedException { Node callNode = getPropNode.getNext(); maybeInitMetaDataFromJsDoc(builder, assignNode); extractFromCallNode(builder, callNode); } /** * Initializes the meta data in a JsMessage by examining the nodes just before * and after a message VAR node. * * @param builder the message builder whose meta data will be initialized * @param varNode the message VAR node * @param parentOfVarNode {@code varNode}'s parent node */ private void maybeInitMetaDataFromJsDocOrHelpVar( Builder builder, Node varNode, @Nullable Node parentOfVarNode) throws MalformedException { // First check description in @desc if (maybeInitMetaDataFromJsDoc(builder, varNode)) { return; } // Check the preceding node for meta data if ((parentOfVarNode != null) && maybeInitMetaDataFromHelpVar(builder, parentOfVarNode.getChildBefore(varNode))) { return; } // Check the subsequent node for meta data maybeInitMetaDataFromHelpVar(builder, varNode.getNext()); } /** * Initializes the meta data in a JsMessage by examining a node just before or * after a message VAR node. * * @param builder the message builder whose meta data will be initialized * @param sibling a node adjacent to the message VAR node * @return true iff message has corresponding description variable */ private boolean maybeInitMetaDataFromHelpVar(Builder builder, @Nullable Node sibling) throws MalformedException { if ((sibling != null) && (sibling.isVar())) { Node nameNode = sibling.getFirstChild(); String name = nameNode.getString(); if (name.equals(builder.getKey() + DESC_SUFFIX)) { Node valueNode = nameNode.getFirstChild(); String desc = extractStringFromStringExprNode(valueNode); if (desc.startsWith(HIDDEN_DESC_PREFIX)) { builder.setDesc(desc.substring(HIDDEN_DESC_PREFIX.length()).trim()); builder.setIsHidden(true); } else {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> a reference to * an unregistered placeholder */ private void parseMessageTextNode(Builder builder, Node node) throws MalformedException { String value = extractStringFromStringExprNode(node); while(true) { int phBegin = value.indexOf(PH_JS_PREFIX); if (phBegin < 0) { // Just a string literal builder.appendStringPart(value); return; } else { if (phBegin > 0) { // A string literal followed by a placeholder builder.appendStringPart(value.substring(0, phBegin)); } // A placeholder. Find where it ends int phEnd = value.indexOf(PH_JS_SUFFIX, phBegin); if (phEnd < 0) { throw new MalformedException( "Placeholder incorrectly formatted in: " + builder.getKey(), node); } String phName = value.substring(phBegin + PH_JS_PREFIX.length(), phEnd); builder.appendPlaceholderReference(phName); int nextPos = phEnd + PH_JS_SUFFIX.length(); if (nextPos < value.length()) { // Iterate on the rest of the message value value = value.substring(nextPos); } else { // The message is parsed return; } } } } /** * Processes found JS message. Several examples of "standard" processing * routines are: * <ol> * <li>extract all JS messages * <li>replace JS messages with localized versions for some specific language * <li>check that messages have correct syntax and present in localization * bundle * </ol> * * @param message the found message * @param definition the definition of the object and usually contains all * additional message information like message node/parent's node */ abstract void processJsMessage(JsMessage message, JsMessageDefinition definition); /** * Returns whether the given JS identifier is a valid JS message name. */ boolean isMessageName(String identifier, boolean isNewStyleMessage) { return identifier.startsWith(MSG_PREFIX) && (style == JsMessage.Style.CLOSURE || isNewStyleMessage || !identifier.endsWith(DESC_SUFFIX)); } /**

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> thousands and thousands of * type invalidations, which makes reporting all of the errors useless. * However, certain properties are worth specifically guarding because of the * large amount of code that can be removed as dead code. This list contains * the properties (eg: "toString") that we care about; if any of these * properties is invalidated it causes an error. */ private final Map<String, CheckLevel> propertiesToErrorFor; private class Property { /** The name of the property. */ final String name; /** All types on which the field exists, grouped together if related. */ private UnionFind<T> types; /** * A set of types for which renaming this field should be skipped. This * list is first filled by fields defined in the externs file. */ Set<T> typesToSkip = Sets.newHashSet(); /** * If true, do not rename any instance of this field, as it has been * referenced from an unknown type. */ boolean skipRenaming; /** Set of nodes for this field that need renaming. */ Set<Node> renameNodes = Sets.newHashSet(); /** * Map from node to the highest type in the prototype chain containing the * field for that node. In the case of a union, the type is the highest type * of one of the types in the union. */ final Map<Node, T> rootTypes = Maps.newHashMap(); Property(String name) { this.name = name; } /** Returns the types on which this field is referenced. */ UnionFind<T> getTypes() { if (types == null) { types = new StandardUnionFind<T>(); } return types; } /** * Record that this property is referenced from this type. * @return true if the type was recorded for this property, else false, * which would happen if the type was invalidating. */ boolean addType(T type, T top, T relatedType) { checkState(!skipRenaming, "Attempt to record skipped property: %s", name); if (typeSystem.isInvalidatingType(top)) { invalidate(); return false; } else { if (typeSystem.isTypeToSkip(top)) { addTypeToSkip

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(top); } if (relatedType == null) { getTypes().add(top); } else { getTypes().union(top, relatedType); } typeSystem.recordInterfaces(type, top, this); return true; } } /** Records the given type as one to skip for this property. */ void addTypeToSkip(T type) { for (T skipType : typeSystem.getTypesToSkipForType(type)) { typesToSkip.add(skipType); getTypes().union(skipType, type); } } /** Invalidates any types related to invalid types. */ void expandTypesToSkip() { // If we are not going to rename any properties, then we do not need to // update the list of invalid types, as they are all invalid. if (shouldRename()) { int count = 0; while (true) { // It should usually only take one time through this do-while. checkState(++count < 10, "Stuck in loop expanding types to skip."); // Make sure that the representative type for each type to skip is // marked as being skipped. Set<T> rootTypesToSkip = Sets.newHashSet(); for (T subType : typesToSkip) { rootTypesToSkip.add(types.find(subType)); } typesToSkip.addAll(rootTypesToSkip); Set<T> newTypesToSkip = Sets.newHashSet(); Set<T> allTypes = types.elements(); int originalTypesSize = allTypes.size(); for (T subType : allTypes) { if (!typesToSkip.contains(subType) && typesToSkip.contains(types.find(subType))) { newTypesToSkip.add(subType); } } for (T newType : newTypesToSkip) { addTypeToSkip(newType); } // If there were not any new types added, we are done here. if (types.elements().size() == originalTypesSize) { break; } } } } /** Returns true if any instance of this property should be renamed. */ boolean shouldRename() { return !skipRenaming && types != null && types

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, String> buildPropNames(UnionFind<T> types, String name) { Map<T, String> names = Maps.newHashMap(); for (Set<T> set : types.allEquivalenceClasses()) { checkState(!set.isEmpty()); String typeName = null; for (T type : set) { if (typeName == null || type.toString().compareTo(typeName) < 0) { typeName = type.toString(); } } String newName; if ("{...}".equals(typeName)) { newName = name; } else { newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name; } for (T type : set) { names.put(type, newName); } } return names; } /** Returns a map from field name to types for which it will be renamed. */ Multimap<String, Collection<T>> getRenamedTypesForTesting() { Multimap<String, Collection<T>> ret = HashMultimap.create(); for (Map.Entry<String, Property> entry: properties.entrySet()) { Property prop = entry.getValue(); if (!prop.skipRenaming) { for (Collection<T> c : prop.getTypes().allEquivalenceClasses()) { if (!c.isEmpty() && !prop.typesToSkip.contains(c.iterator().next())) { ret.put(entry.getKey(), c); } } } } return ret; } /** Interface for providing the type information needed by this pass. */ private interface TypeSystem<T> { // TODO(user): add a getUniqueName(T type) method that is guaranteed // to be unique, performant and human-readable. /** Returns the top-most scope used by the type system (if any). */ StaticScope<T> getRootScope(); /** Returns the new scope started at the given function node. */ StaticScope<T> getFunctionScope(Node node); /** * Returns the type of the given node. * @param prop Only types with this property need to be returned. In general * with type tightening, this will require no special processing, but in * the case of an unknown JSType, we might need to add in the native

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> public JSTypeSystem(AbstractCompiler compiler) { registry = compiler.getTypeRegistry(); invalidatingTypes = Sets.newHashSet( registry.getNativeType(JSTypeNative.ALL_TYPE), registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE), registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); } @Override public void addInvalidatingType(JSType type) { checkState(!type.isUnionType()); invalidatingTypes.add(type); } @Override public StaticScope<JSType> getRootScope() { return null; } @Override public StaticScope<JSType> getFunctionScope(Node node) { return null; } @Override public JSType getType( StaticScope<JSType> scope, Node node, String prop) { if (node.getJSType() == null) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return node.getJSType(); } @Override public boolean isInvalidatingType(JSType type) { if (type == null || invalidatingTypes.contains(type) || type.isUnknownType() /* unresolved types */) { return true; } ObjectType objType = ObjectType.cast(type); return objType != null && !objType.hasReferenceName(); } @Override public ImmutableSet<JSType> getTypesToSkipForType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { Set<JSType> types = Sets.newHashSet(type); for (JSType alt : type.toMaybeUnionType().getAlternates()) { types.addAll(getTypesToSkipForTypeNonUnion(type)); } return ImmutableSet.copyOf(types); } else if (type.isEnumElementType()) { return getTypesToSkipForType( type

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> (constructor != null) { for (ObjectType itype : constructor.getImplementedInterfaces()) { JSType top = getTypeWithProperty(p.name, itype); if (top != null) { p.addType(itype, top, relatedType); } else { recordInterfaces(itype, relatedType, p); } // If this interface invalidated this property, return now. if (p.skipRenaming) return; } if (constructor.isInterface() || constructor.isConstructor()) { constructor = constructor.getSuperClassConstructor(); } else { constructor = null; } } } } } /** Implementation of TypeSystem using concrete types. */ private static class ConcreteTypeSystem implements TypeSystem<ConcreteType> { private final TightenTypes tt; private int nextUniqueId; private CodingConvention codingConvention; private final Set<JSType> invalidatingTypes = Sets.newHashSet(); // An array of native types that are not tracked by type tightening, and // thus need to be added in if an unknown type is encountered. private static final JSTypeNative [] nativeTypes = new JSTypeNative[] { JSTypeNative.BOOLEAN_OBJECT_TYPE, JSTypeNative.NUMBER_OBJECT_TYPE, JSTypeNative.STRING_OBJECT_TYPE }; public ConcreteTypeSystem(TightenTypes tt, CodingConvention convention) { this.tt = tt; this.codingConvention = convention; } @Override public void addInvalidatingType(JSType type) { checkState(!type.isUnionType()); invalidatingTypes.add(type); } @Override public StaticScope<ConcreteType> getRootScope() { return tt.getTopScope(); } @Override public StaticScope<ConcreteType> getFunctionScope(Node decl) { ConcreteFunctionType func = tt.getConcreteFunction(decl); return (func != null) ? func.getScope() : (StaticScope<ConcreteType>) null; } @Override public ConcreteType getType( StaticScope<ConcreteType> scope, Node node, String prop) { if (scope != null) { ConcreteType c = tt.inferConcreteType( (TightenTypes.ConcreteScope) scope, node); return maybeAddAuto

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, or function literals. JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(node); if (jsdoc == null && !node.isFunction()) { return ""; } JSType type = node.getJSType(); if (type == null) { return ""; } else if (type.isFunctionType()) { return getFunctionAnnotation(node); } else if (type.isEnumType()) { return "/** @enum {" + type.toMaybeEnumType().getElementsType().toAnnotationString() + "} */\n"; } else if (!type.isUnknownType() && !type.isEmptyType() && !type.isVoidType() && !type.isFunctionPrototypeType()) { return "/** @type {" + node.getJSType().toAnnotationString() + "} */\n"; } else { return ""; } } /** * @param fnNode A node for a function for which to generate a type annotation */ private String getFunctionAnnotation(Node fnNode) { Preconditions.checkState(fnNode.isFunction()); StringBuilder sb = new StringBuilder("/**\n"); JSType type = fnNode.getJSType(); if (type == null || type.isUnknownType()) { return ""; } FunctionType funType = type.toMaybeFunctionType(); // We need to use the child nodes of the function as the nodes for the // parameters of the function type do not have the real parameter names. // FUNCTION // NAME // LP // NAME param1 // NAME param2 if (fnNode != null) { Node paramNode = NodeUtil.getFunctionParameters(fnNode).getFirstChild(); // Param types for (Node n : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } sb.append(" * "); appendAnnotation(sb, "param", getParameterNodeJSDocType(n)); sb.append(" ") .append(paramNode.getString()) .append("\n"); paramNode = paramNode.getNext(); } } // Return type JSType retType = funType.getReturnType(); if (retType != null && !

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> we model scopes but handles some // additional cases that are not handled by the current Scope object. // Specifically, a Scope currently has only two concepts of scope (global, // and function local). But there are in reality a couple of additional // case to worry about: // catch expressions // function expressions names // Both belong to a scope by themselves. private Deque<Renamer> nameStack = new ArrayDeque<Renamer>(); private final Renamer rootRenamer; MakeDeclaredNamesUnique() { this(new ContextualRenamer()); } MakeDeclaredNamesUnique(Renamer renamer) { this.rootRenamer = renamer; } static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) { return new ContextualRenameInverter(compiler); } @Override public void enterScope(NodeTraversal t) { Node declarationRoot = t.getScopeRoot(); Renamer renamer; if (nameStack.isEmpty()) { // If the contextual renamer is being used, the starting context can not // be a function. Preconditions.checkState( !declarationRoot.isFunction() || !(rootRenamer instanceof ContextualRenamer)); Preconditions.checkState(t.inGlobalScope()); renamer = rootRenamer; } else { renamer = nameStack.peek().forChildScope(); } if (!declarationRoot.isFunction()) { // Add the block declarations findDeclaredNames(declarationRoot, null, renamer); } nameStack.push(renamer); } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { nameStack.pop(); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.FUNCTION: { // Add recursive function name, if needed. // NOTE: "enterScope" is called after we need to pick up this name. Renamer renamer = nameStack.peek().forChildScope(); // If needed, add the function recursive name. String name = n.getFirstChild().getString(); if (name != null && !name.isEmpty() && parent != null &&

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>containsSeparator(name) && !getOrginalName(name).isEmpty()) { String newName = findReplacementName(name); referencedNames.remove(name); // Adding a reference to the new name to prevent either the parent // scopes or the current scope renaming another var to this new name. referencedNames.add(newName); List<Node> references = nameMap.get(name); Preconditions.checkState(references != null); for (Node n : references) { Preconditions.checkState(n.isName()); n.setString(newName); } compiler.reportCodeChange(); nameMap.remove(name); } } /** * Find a name usable in the local scope. */ private String findReplacementName(String name) { String original = getOrginalName(name); String newName = original; int i = 0; while (!isValidName(newName)) { newName = original + ContextualRenamer.UNIQUE_ID_SEPARATOR + String.valueOf(i++); } return newName; } /** * @return Whether the name is valid to use in the local scope. */ private boolean isValidName(String name) { if (TokenStream.isJSIdentifier(name) && !referencedNames.contains(name) && !name.equals(ARGUMENTS)) { return true; } return false; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node node, Node parent) { if (t.inGlobalScope()) { return; } if (NodeUtil.isReferenceName(node)) { String name = node.getString(); // Add all referenced names to the set so it is possible to check for // conflicts. referencedNames.add(name); // Store only references to candidate names in the node map. if (containsSeparator(name)) { addCandidateNameReference(name, node); } } } private void addCandidateNameReference(String name, Node n) { List<Node> nodes = nameMap.get(name); if (null == nodes) { nodes = Lists.newLinkedList(); nameMap.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, 0, 1); } private int incrementNameCount(String name) { return nameUsage.add(name, 1); } @Override public boolean stripConstIfReplaced() { return false; } } /** * Rename every declared name to be unique. Typically this would be used * when injecting code to insure that names do not conflict with existing * names. * * Used by the FunctionInjector * @see FunctionInjector */ static class InlineRenamer implements Renamer { private final Map<String, String> declarations = Maps.newHashMap(); private final Supplier<String> uniqueIdSupplier; private final String idPrefix; private final boolean removeConstness; InlineRenamer( Supplier<String> uniqueIdSupplier, String idPrefix, boolean removeConstness) { this.uniqueIdSupplier = uniqueIdSupplier; // To ensure that the id does not conflict with the id from the // ContextualRenamer some prefix is needed. Preconditions.checkArgument(!idPrefix.isEmpty()); this.idPrefix = idPrefix; this.removeConstness = removeConstness; } @Override public void addDeclaredName(String name) { Preconditions.checkState(!name.equals(ARGUMENTS)); if (!declarations.containsKey(name)) { declarations.put(name, getUniqueName(name)); } } private String getUniqueName(String name) { if (name.isEmpty()) { return name; } if (name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1) { name = name.substring( 0, name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR)); } // By using the same separator the id will be stripped if it isn't // needed when variable renaming is turned off. return name + ContextualRenamer.UNIQUE_ID_SEPARATOR + idPrefix + uniqueIdSupplier.get(); } @Override public String getReplacementName(String oldName) { return declarations.get(oldName); } @Override public Renamer forChildScope() { return new InlineRenamer(uniqueIdSupplier, idPrefix, removeConstness); } @Override public boolean stripConstIfReplaced()

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> where * possible. */ private void maybeCollapseIntoForStatements(Node n, Node parent) { // Only SCRIPT, BLOCK, and LABELs can have FORs that can be collapsed into. // LABELs are not supported here. if (parent == null || !NodeUtil.isStatementBlock(parent)) { return; } // Is the current node something that can be in a for loop initializer? if (!n.isExprResult() && !n.isVar()) { return; } // Is the next statement a valid FOR? Node nextSibling = n.getNext(); if (nextSibling == null) { return; } else if (NodeUtil.isForIn(nextSibling)) { Node forNode = nextSibling; Node forVar = forNode.getFirstChild(); if (forVar.isName() && n.isVar() && n.hasOneChild()) { Node name = n.getFirstChild(); if (!name.hasChildren() && forVar.getString().equals(name.getString())) { // OK, the names match, and the var declaration does not have an // initializer. Move it into the loop. parent.removeChild(n); forNode.replaceChild(forVar, n); compiler.reportCodeChange(); } } } else if (nextSibling.isFor() && nextSibling.getFirstChild().isEmpty()) { // Does the current node contain an in operator? If so, embedding // the expression in a for loop can cause some JavaScript parsers (such // as the PlayStation 3's browser based on Access's NetFront // browser) to fail to parse the code. // See bug 1778863 for details. if (NodeUtil.containsType(n, Token.IN)) { return; } // Move the current node into the FOR loop initializer. Node forNode = nextSibling; Node oldInitializer = forNode.getFirstChild(); parent.removeChild(n); Node newInitializer; if (n.isVar()) { newInitializer = n; } else { // Extract the expression from EXPR_RESULT node. Preconditions.checkState(n.hasOneChild()); newInitializer = n.getFirstChild(); n.removeChild(newInitializer);

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> isConstant = isPropertyDeclaredConstant(objectType, propertyName); // Check whether constant properties are reassigned if (isConstant) { if (isDelete) { compiler.report( t.makeError(getprop, CONST_PROPERTY_DELETED, propertyName)); return; } ObjectType oType = objectType; while (oType != null) { if (oType.hasReferenceName()) { if (initializedConstantProperties.containsEntry( oType.getReferenceName(), propertyName)) { compiler.report( t.makeError(getprop, CONST_PROPERTY_REASSIGNED_VALUE, propertyName)); break; } } oType = oType.getImplicitPrototype(); } Preconditions.checkState(objectType.hasReferenceName()); initializedConstantProperties.put(objectType.getReferenceName(), propertyName); // Add the prototype when we're looking at an instance object if (objectType.isInstanceType()) { ObjectType prototype = objectType.getImplicitPrototype(); if (prototype != null) { if (prototype.hasProperty(propertyName) && prototype.hasReferenceName()) { initializedConstantProperties.put(prototype.getReferenceName(), propertyName); } } } } } /** * Determines whether the given property is visible in the current context. * @param t The current traversal. * @param getprop The getprop node. */ private void checkPropertyVisibility(NodeTraversal t, Node getprop, Node parent) { ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); if (objectType != null) { // Is this a normal property access, or are we trying to override // an existing property? boolean isOverride = parent.getJSDocInfo() != null && parent.isAssign() && parent.getFirstChild() == getprop; // Find the lowest property defined on a class with visibility // information. if (isOverride) { objectType = objectType.getImplicitPrototype(); } JSDocInfo docInfo = null; for (; objectType != null; objectType = objectType.getImplicitPrototype()) { docInfo = objectType.getOwnPropertyJSDocInfo(

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Code) { sharedCallbacks.add(new CheckAccidentalSemicolon(CheckLevel.WARNING)); } if (options.enables(DiagnosticGroups.GLOBAL_THIS)) { sharedCallbacks.add(new CheckGlobalThis(compiler)); } if (options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) { sharedCallbacks.add(new CheckDebuggerStatement(compiler)); } return combineChecks(compiler, sharedCallbacks); } }; /** Verify that all the passes are one-time passes. */ private void assertAllOneTimePasses(List<PassFactory> passes) { for (PassFactory pass : passes) { Preconditions.checkState(pass.isOneTimePass()); } } /** Verify that all the passes are multi-run passes. */ private void assertAllLoopablePasses(List<PassFactory> passes) { for (PassFactory pass : passes) { Preconditions.checkState(!pass.isOneTimePass()); } } /** Checks for validity of the control structures. */ final HotSwapPassFactory checkControlStructures = new HotSwapPassFactory("checkControlStructures", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new ControlStructureCheck(compiler); } }; /** Checks that all constructed classes are goog.require()d. */ final HotSwapPassFactory checkRequires = new HotSwapPassFactory("checkRequires", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new CheckRequiresForConstructors(compiler, options.checkRequires); } }; /** Makes sure @constructor is paired with goog.provides(). */ final HotSwapPassFactory checkProvides = new HotSwapPassFactory("checkProvides", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new CheckProvides(compiler, options.checkProvides); } }; private static final DiagnosticType GENERATE_EXPORTS_ERROR = DiagnosticType.error( "JSC_GENERATE_EXPORTS_ERROR", "Exports can only be generated if export symbol/property " + "functions are set."); /** Generates exports for @export annotations. */ final PassFactory generateExports = new PassFactory("generateExports",

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>getExportedVariableNames(); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { pass.hotSwapScript(scriptRoot, originalRoot); } }; } }; /** Expand jQuery Primitives and Aliases pass. */ final PassFactory jqueryAliases = new PassFactory("jqueryAliases", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new ExpandJqueryAliases(compiler); } }; /** * The default i18n pass. * A lot of the options are not configurable, because ReplaceMessages * has a lot of legacy logic. */ final PassFactory replaceMessages = new PassFactory("replaceMessages", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new ReplaceMessages(compiler, options.messageBundle, /* warn about message dupes */ true, /* allow messages with goog.getMsg */ JsMessage.Style.getFromParams(true, false), /* if we can't find a translation, don't worry about it. */ false); } }; /** Applies aliases and inlines goog.scope. */ final HotSwapPassFactory closureGoogScopeAliases = new HotSwapPassFactory("processGoogScopeAliases", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { maybeInitializePreprocessorSymbolTable(compiler); return new ScopedAliases( compiler, preprocessorSymbolTable, options.getAliasTransformationHandler()); } }; /** Checks that CSS class names are wrapped in goog.getCssName */ final PassFactory closureCheckGetCssName = new PassFactory("checkMissingGetCssName", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { String blacklist = options.checkMissingGetCssNameBlacklist; Preconditions.checkState(blacklist != null && !blacklist.isEmpty(), "Not checking use of goog.getCssName because of empty blacklist."); return new CheckMissingGetCssName( compiler, options.checkMissingGetCssNameLevel, blacklist); } }; /** * Processes goog.getCssName. The cssRenamingMap is used to lookup * replacement values for the classnames. If null, the raw class

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>()); } }; /** Checks that all variables are defined. */ final HotSwapPassFactory checkVars = new HotSwapPassFactory("checkVars", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new VarCheck(compiler); } }; /** Checks for RegExp references. */ final PassFactory checkRegExp = new PassFactory("checkRegExp", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { final CheckRegExp pass = new CheckRegExp(compiler); return new CompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); compiler.setHasRegExpGlobalReferences( pass.isGlobalRegExpPropertiesUsed()); } }; } }; /** Checks that references to variables look reasonable. */ final HotSwapPassFactory checkVariableReferences = new HotSwapPassFactory("checkVariableReferences", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new VariableReferenceCheck( compiler, options.aggressiveVarCheck); } }; /** Pre-process goog.testing.ObjectPropertyString. */ final PassFactory objectPropertyStringPreprocess = new PassFactory("ObjectPropertyStringPreprocess", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new ObjectPropertyStringPreprocess(compiler); } }; /** Creates a typed scope and adds types to the type registry. */ final HotSwapPassFactory resolveTypes = new HotSwapPassFactory("resolveTypes", false) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new GlobalTypeResolver(compiler); } }; /** Runs type inference. */ final HotSwapPassFactory inferTypes = new HotSwapPassFactory("inferTypes", false) { @Override protected HotSwapCompilerPass createInternal(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); makeTypeInference(compiler).process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) { makeTypeInference(compiler).inferTypes(scriptRoot); } }; } }; final HotSwapPassFactory inferJsDocInfo = new HotSwapPassFactory("inferJsDocInfo", false) { @Override protected HotSwapCompilerPass createInternal( final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); makeInferJsDocInfo(compiler).process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeInferJsDocInfo(compiler).hotSwapScript(scriptRoot, originalRoot); } }; } }; /** Checks type usage */ final HotSwapPassFactory checkTypes = new HotSwapPassFactory("checkTypes", false) { @Override protected HotSwapCompilerPass createInternal(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); TypeCheck check = makeTypeCheck(compiler); check.process(externs, root); compiler.getErrorManager().setTypedPercent(check.getTypedPercent()); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeTypeCheck(compiler).check(scriptRoot, false); } }; } }; /** * Checks possible execution paths of the program for problems: missing return * statements and dead code. */ final HotSwapPassFactory checkControlFlow = new HotSwapPassFactory("checkControlFlow", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { List<Callback> callbacks = Lists.newArrayList(); if (options.checkUnreachableCode.isOn()) { callbacks.add( new CheckUnreachableCode(compiler, options.checkUnreachableCode)); } if (options.checkMissingReturn.isOn() && options.checkTypes) { callbacks.add( new CheckMissingReturn(compiler, options.checkMissingReturn)); } return combineChecks(compiler, callbacks);

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> } }; /** Checks access controls. Depends on type-inference. */ final HotSwapPassFactory checkAccessControls = new HotSwapPassFactory("checkAccessControls", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new CheckAccessControls(compiler); } }; /** Executes the given callbacks with a {@link CombinedCompilerPass}. */ private static HotSwapCompilerPass combineChecks(AbstractCompiler compiler, List<Callback> callbacks) { Preconditions.checkArgument(callbacks.size() > 0); Callback[] array = callbacks.toArray(new Callback[callbacks.size()]); return new CombinedCompilerPass(compiler, array); } /** A compiler pass that resolves types in the global scope. */ class GlobalTypeResolver implements HotSwapCompilerPass { private final AbstractCompiler compiler; GlobalTypeResolver(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { if (topScope == null) { regenerateGlobalTypedScope(compiler, root.getParent()); } else { compiler.getTypeRegistry().resolveTypesInScope(topScope); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { patchGlobalTypedScope(compiler, scriptRoot); } } /** Checks global name usage. */ final PassFactory checkGlobalNames = new PassFactory("checkGlobalNames", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { // Create a global namespace for analysis by check passes. // Note that this class does all heavy computation lazily, // so it's OK to create it here. namespaceForChecks = new GlobalNamespace(compiler, externs, jsRoot); new CheckGlobalNames(compiler, options.checkGlobalNamesLevel) .injectNamespace(namespaceForChecks).process(externs, jsRoot); } }; } }; /** Checks that the code is ES5 or Caja compliant. */ final PassFactory checkStrictMode = new PassFactory("checkStrictMode", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler)

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> literal cast provides a mechanism to cast object literals to * other types without a warning. */ static class ObjectLiteralCast { /** Type to cast to. */ final String typeName; /** Object to cast. */ final Node objectNode; /** Error message */ final DiagnosticType diagnosticType; ObjectLiteralCast(String typeName, Node objectNode, DiagnosticType diagnosticType) { this.typeName = typeName; this.objectNode = objectNode; this.diagnosticType = diagnosticType; } } /** * A function that will throw an exception when either: * -One or more of its parameters evaluate to false. * -One or more of its parameters are not of a certain type. */ public class AssertionFunctionSpec { protected final String functionName; protected final JSTypeNative assertedType; public AssertionFunctionSpec(String functionName) { this(functionName, null); } public AssertionFunctionSpec(String functionName, JSTypeNative assertedType) { this.functionName = functionName; this.assertedType = assertedType; } /** Returns the name of the function. */ public String getFunctionName() { return functionName; } /** * Returns the parameter of the assertion function that is being checked. * @param firstParam The first parameter of the function call. */ public Node getAssertedParam(Node firstParam) { return firstParam; } /** * Returns the type for a type assertion, or null if the function asserts * that the node must not be null or undefined. */ public JSType getAssertedType(Node call, JSTypeRegistry registry) { return assertedType != null ? registry.getNativeType(assertedType) : null; } } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { TypePair p = element.getTypesUnderShallowInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseUnionType(this); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setResolvedTypeInternal(this); // for circularly defined types. boolean changed = false; ImmutableList.Builder<JSType> resolvedTypes = ImmutableList.builder(); for (JSType alternate : alternates) { JSType newAlternate = alternate.resolve(t, scope); changed |= (alternate != newAlternate); resolvedTypes.add(alternate); } if (changed) { Collection<JSType> newAlternates = resolvedTypes.build(); Preconditions.checkState( newAlternates.hashCode() == this.hashcode); alternates = newAlternates; } return this; } @Override public String toDebugHashCodeString() { List<String> hashCodes = Lists.newArrayList(); for (JSType a : alternates) { hashCodes.add(a.toDebugHashCodeString()); } return "{(" + Joiner.on(",").join(hashCodes) + ")}"; } @Override public boolean setValidator(Predicate<JSType> validator) { for (JSType a : alternates) { a.setValidator(validator); } return true; } @Override public JSType collapseUnion() { JSType currentValue = null; ObjectType currentCommonSuper = null; for (JSType a : alternates) { if (a.isUnknownType()) { return getNativeType(JSTypeNative.UNKNOWN_TYPE); } ObjectType obj = a.toObjectType

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> continue retry; case EXPOSE: if (!jsdocBuilder.recordExpose()) { parser.addParserWarning("msg.jsdoc.expose", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case EXTERNS: if (!jsdocBuilder.recordExterns()) { parser.addParserWarning("msg.jsdoc.externs", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case JAVA_DISPATCH: if (!jsdocBuilder.recordJavaDispatch()) { parser.addParserWarning("msg.jsdoc.javadispatch", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case EXTENDS: case IMPLEMENTS: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); boolean matchingRc = false; if (token == JsDocToken.LC) { token = next(); matchingRc = true; } if (token == JsDocToken.STRING) { Node typeNode = parseAndRecordTypeNameNode( token, lineno, charno, matchingRc); lineno = stream.getLineno(); charno = stream.getCharno(); typeNode = wrapNode(Token.BANG, typeNode); if (typeNode != null && !matchingRc) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } type = createJSTypeExpression(typeNode); if (annotation == Annotation.EXTENDS) { // record the extended type, check later extendedTypes.add(new ExtendedTypeInfo( type, stream.getLineno(), stream.getCharno())); } else { Preconditions.checkState( annotation == Annotation.IMPLEMENTS); if (!jsdocBuilder.recordImplementedInterface(type)) { parser.addTypeWarning("msg.jsdoc.implements.duplicate", lineno, charno); } } token = next(); if (matchingRc) { if (token != JsDocToken.RC) { parser.addTypeWarning("msg.jsdoc.missing.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> The type expression found or null if none. */ private Node parseAndRecordTypeNameNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC) { return parseAndRecordTypeNode(token, lineno, startCharno, matchingLC, true); } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * Parameter type expressions are special for two reasons: * <ol> * <li>They must begin with '{', to distinguish type names from param names. * <li>They may end in '=', to denote optionality. * </ol> * * @param token The current token. * @return The type expression found or null if none. */ private Node parseAndRecordParamTypeNode(JsDocToken token) { Preconditions.checkArgument(token == JsDocToken.LC); int lineno = stream.getLineno(); int startCharno = stream.getCharno(); Node typeNode = parseParamTypeExpressionAnnotation(token); if (typeNode != null) { int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, endLineno, endCharno, true); } return typeNode; } /** * Looks for a parameter type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param lineno The line of the type expression. * @param startCharno The starting character position of the type expression. * @param matchingLC Whether the type expression starts with a "{". * @param onlyParseSimpleNames If true, only simple type names are parsed * (via a call to parseTypeNameAnnotation instead of * parseTypeExpressionAnnotation). * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC, boolean onlyParseSimpleNames) { Node typeNode = null; if (onlyParseSimpleNames) { typeNode = parse

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> { return parseTypeExpression(token); } } /** * ParamTypeExpressionAnnotation := * '{' OptionalParameterType '}' | * '{' TopLevelTypeExpression '}' | * '{' '...' TopLevelTypeExpression '}' * * OptionalParameterType := * TopLevelTypeExpression '=' */ private Node parseParamTypeExpressionAnnotation(JsDocToken token) { Preconditions.checkArgument(token == JsDocToken.LC); skipEOLs(); boolean restArg = false; token = next(); if (token == JsDocToken.ELLIPSIS) { token = next(); if (token == JsDocToken.RC) { // EMPTY represents the UNKNOWN type in the Type AST. return wrapNode(Token.ELLIPSIS, IR.empty()); } restArg = true; } Node typeNode = parseTopLevelTypeExpression(token); if (typeNode != null) { skipEOLs(); if (restArg) { typeNode = wrapNode(Token.ELLIPSIS, typeNode); } else if (match(JsDocToken.EQUALS)) { next(); skipEOLs(); typeNode = wrapNode(Token.EQUALS, typeNode); } if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } /** * TypeNameAnnotation := TypeName | '{' TypeName '}' */ private Node parseTypeNameAnnotation(JsDocToken token) { if (token == JsDocToken.LC) { skipEOLs(); Node typeNode = parseTypeName(next()); if (typeNode != null) { skipEOLs(); if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } else { return parseTypeName(token); } } /** * TopLevelTypeExpression := TypeExpression * | TypeUnionList * * We made this rule up, for the sake of backwards compatibility. */ private Node parseTopLevelTypeExpression(JsDocToken token) { Node

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> token) { if (token != JsDocToken.STRING) { return reportGenericTypeSyntaxWarning(); } String typeName = stream.getString(); int lineno = stream.getLineno(); int charno = stream.getCharno(); while (match(JsDocToken.EOL) && typeName.charAt(typeName.length() - 1) == '.') { skipEOLs(); if (match(JsDocToken.STRING)) { next(); typeName += stream.getString(); } } Node typeNameNode = newStringNode(typeName, lineno, charno); if (match(JsDocToken.LT)) { next(); skipEOLs(); Node memberType = parseTypeExpressionList(next()); if (memberType != null) { typeNameNode.addChildToFront(memberType); skipEOLs(); if (!match(JsDocToken.GT)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.gt"); } next(); } } return typeNameNode; } /** * FunctionType := 'function' FunctionSignatureType * FunctionSignatureType := * TypeParameters '(' 'this' ':' TypeName, ParametersType ')' ResultType */ private Node parseFunctionType(JsDocToken token) { // NOTE(nicksantos): We're not implementing generics at the moment, so // just throw out TypeParameters. if (token != JsDocToken.LP) { restoreLookAhead(token); return reportTypeSyntaxWarning("msg.jsdoc.missing.lp"); } Node functionType = newNode(Token.FUNCTION); Node parameters = null; skipEOLs(); if (!match(JsDocToken.RP)) { token = next(); boolean hasParams = true; if (token == JsDocToken.STRING) { String tokenStr = stream.getString(); boolean isThis = "this".equals(tokenStr); boolean isNew = "new".equals(tokenStr); if (isThis || isNew) { if (match(JsDocToken.COLON)) { next(); skipEOLs(); Node contextType = wrapNode( isThis ? Token.THIS : Token.NEW, parseTypeName(next())); if (contextType == null) { return null;

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.VOID); } else { return parseTypeExpression(next()); } } /** * UnionType := '(' TypeUnionList ')' * TypeUnionList := TypeExpression | TypeExpression '|' TypeUnionList * * We've removed the empty union type. */ private Node parseUnionType(JsDocToken token) { return parseUnionTypeWithAlternate(token, null); } /** * Create a new union type, with an alternate that has already been * parsed. The alternate may be null. */ private Node parseUnionTypeWithAlternate(JsDocToken token, Node alternate) { Node union = newNode(Token.PIPE); if (alternate != null) { union.addChildToBack(alternate); } Node expr = null; do { if (expr != null) { skipEOLs(); token = next(); Preconditions.checkState( token == JsDocToken.PIPE || token == JsDocToken.COMMA); boolean isPipe = token == JsDocToken.PIPE; if (isPipe && match(JsDocToken.PIPE)) { // We support double pipes for backwards compatibility. next(); } skipEOLs(); token = next(); } expr = parseTypeExpression(token); if (expr == null) { return null; } union.addChildToBack(expr); // We support commas for backwards compatibility. } while (match(JsDocToken.PIPE, JsDocToken.COMMA)); if (alternate == null) { skipEOLs(); if (!match(JsDocToken.RP)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); } next(); } return union; } /** * ArrayType := '[' ElementTypeList ']' * ElementTypeList := <empty> | TypeExpression | '...' TypeExpression * | TypeExpression ',' ElementTypeList */ private Node parseArrayType(JsDocToken token) { Node array = newNode(Token.LB); Node arg = null; boolean hasVarArgs = false; do { if (arg != null) { next(); skipEOLs(); token = next(); } if (token == JsDocToken.ELLIPSIS) { arg = wrapNode(Token.ELLIPS

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>>true</code> if it exist. */ public final boolean hasNode(N n) { return getNode(n) != null; } /** * Checks whether two nodes in the graph are connected. * * @param n1 Node 1. * @param n2 Node 2. * @return <code>true</code> if the two nodes are connected. */ public abstract boolean isConnected(N n1, N n2); /** * Checks whether two nodes in the graph are connected by the given * edge type. * * @param n1 Node 1. * @param e The edge type. * @param n2 Node 2. */ public abstract boolean isConnected(N n1, E e, N n2); /** * Gets the node of the specified type, or throws an * IllegalArgumentException. */ @SuppressWarnings("unchecked") <T extends GraphNode<N, E>> T getNodeOrFail(N val) { T node = (T) getNode(val); if (node == null) { throw new IllegalArgumentException(val + " does not exist in graph"); } return node; } @Override public final void clearNodeAnnotations() { for (GraphNode<N, E> n : getNodes()) { n.setAnnotation(null); } } /** Makes each edge's annotation null. */ public final void clearEdgeAnnotations() { for (GraphEdge<N, E> e : getEdges()) { e.setAnnotation(null); } } /** * Pushes nodes' annotation values. Restored with * {@link #popNodeAnnotations()}. Nodes' annotation values are cleared. */ public final void pushNodeAnnotations() { if (nodeAnnotationStack == null) { nodeAnnotationStack = Lists.newLinkedList(); } pushAnnotations(nodeAnnotationStack, getNodes()); } /** * Restores nodes' annotation values to state before last * {@link #pushNodeAnnotations()}. */ public final void popNodeAnnotations() { Preconditions.checkNotNull(nodeAnnotationStack, "Popping node annotations without pushing."); popAnnotations(nodeAnnotationStack); } /** * Pushes edges' annotation values. Restored with * {@link #popEdge

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Annotations()}. Edges' annotation values are cleared. */ public final void pushEdgeAnnotations() { if (edgeAnnotationStack == null) { edgeAnnotationStack = Lists.newLinkedList(); } pushAnnotations(edgeAnnotationStack, getEdges()); } /** * Restores edges' annotation values to state before last * {@link #pushEdgeAnnotations()}. */ public final void popEdgeAnnotations() { Preconditions.checkNotNull(edgeAnnotationStack, "Popping edge annotations without pushing."); popAnnotations(edgeAnnotationStack); } /** * A generic edge. * * @param <N> Value type that the graph node stores. * @param <E> Value type that the graph edge stores. */ public interface GraphEdge<N, E> extends Annotatable { /** * Retrieves the edge's value. * * @return The value. */ E getValue(); GraphNode<N, E> getNodeA(); GraphNode<N, E> getNodeB(); } /** * A simple implementation of SubGraph that calculates adjacency by iterating * over a node's neighbors. */ class SimpleSubGraph<N, E> implements SubGraph<N, E> { private Graph<N, E> graph; private List<GraphNode<N, E>> nodes = Lists.newArrayList(); SimpleSubGraph(Graph<N, E> graph) { this.graph = graph; } @Override public boolean isIndependentOf(N value) { GraphNode<N, E> node = graph.getNode(value); for (GraphNode<N, E> n : nodes) { if (graph.getNeighborNodes(n.getValue()).contains(node)) { return false; } } return true; } @Override public void addNode(N value) { nodes.add(graph.getNodeOrFail(value)); } } /** * Pushes a new list on stack and stores nodes annotations in the new list. * Clears objects' annotations as well. */ private static void pushAnnotations( Deque<GraphAnnotationState> stack, Collection<? extends Annotatable> haveAnnotations) { stack.push(new GraphAnnotationState(haveAnnotations.size())); for (Annotatable h : haveAnnotations

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, name, isPropAssign, type); } else { handleSetFromLocal(t, n, parent, name); } } else { handleGet(t, n, parent, name); } } /** * Gets the fully qualified name corresponding to an object literal key, * as long as it and its prefix property names are valid JavaScript * identifiers. The object literal may be nested inside of other object * literals. * * For example, if called with node {@code n} representing "z" in any of * the following expressions, the result would be "w.x.y.z": * <code> var w = {x: {y: {z: 0}}}; </code> * <code> w.x = {y: {z: 0}}; </code> * <code> w.x.y = {'a': 0, 'z': 0}; </code> * * @param n A child of an OBJLIT node * @return The global name, or null if {@code n} doesn't correspond to the * key of an object literal that can be named */ String getNameForObjLitKey(Node n) { Node parent = n.getParent(); Preconditions.checkState(parent.isObjectLit()); Node gramps = parent.getParent(); if (gramps == null) { return null; } Node greatGramps = gramps.getParent(); String name; switch (gramps.getType()) { case Token.NAME: // VAR // NAME (gramps) // OBJLIT (parent) // STRING (n) if (greatGramps == null || !greatGramps.isVar()) { return null; } name = gramps.getString(); break; case Token.ASSIGN: // ASSIGN (gramps) // NAME|GETPROP // OBJLIT (parent) // STRING (n) Node lvalue = gramps.getFirstChild(); name = lvalue.getQualifiedName(); break; case Token.STRING_KEY: // OBJLIT // STRING (gramps) // OBJLIT (parent) //

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> != null) { for (Name n : props) { if (!n.canCollapse()) { return false; } } } return true; } boolean isSimpleStubDeclaration() { if (getRefs().size() == 1) { Ref ref = refs.get(0); JSDocInfo info = ref.node.getJSDocInfo(); if (ref.node.getParent() != null && ref.node.getParent().isExprResult()) { return true; } } return false; } boolean canCollapse() { return !inExterns && !isGetOrSetDefinition() && (declaredType || (parent == null || parent.canCollapseUnannotatedChildNames()) && (globalSets > 0 || localSets > 0) && deleteProps == 0); } boolean isGetOrSetDefinition() { return this.type == Type.GET || this.type == Type.SET; } boolean canCollapseUnannotatedChildNames() { if (type == Type.OTHER || isGetOrSetDefinition() || globalSets != 1 || localSets != 0 || deleteProps != 0) { return false; } // Don't try to collapse if the one global set is a twin reference. // We could theoretically handle this case in CollapseProperties, but // it's probably not worth the effort. Preconditions.checkNotNull(declaration); if (declaration.getTwin() != null) { return false; } if (declaredType) { return true; } // If this is a key of an aliased object literal, then it will be aliased // later. So we won't be able to collapse its properties. if (parent != null && parent.shouldKeepKeys()) { return false; } // If this is aliased, then its properties can't be collapsed either. if (aliasingGets > 0) { return false; } return (parent == null || parent.canCollapseUnannotatedChildNames()); } /** Whether this is an object literal that needs to keep its keys. */ boolean shouldKeepKeys() { return type == Type.OBJECTLIT && aliasingGets > 0; } boolean needsToBeStubbed() {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>() { return source == null ? "" : source.getName(); } Ref getTwin() { return twin; } boolean isSet() { return type == Type.SET_FROM_GLOBAL || type == Type.SET_FROM_LOCAL; } static void markTwins(Ref a, Ref b) { Preconditions.checkArgument( (a.type == Type.ALIASING_GET || b.type == Type.ALIASING_GET) && (a.type == Type.SET_FROM_GLOBAL || a.type == Type.SET_FROM_LOCAL || b.type == Type.SET_FROM_GLOBAL || b.type == Type.SET_FROM_LOCAL)); a.twin = b; b.twin = a; } /** * Create a new ref that is the same as this one, but of * a different class. */ Ref cloneAndReclassify(Type type) { return new Ref(this, type, this.preOrderIndex); } static Ref createRefForTesting(Type type) { return new Ref(type, -1); } } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> this(ast, ast.getSourceFile().getName(), false); } public CompilerInput(SourceAst ast, boolean isExtern) { this(ast, ast.getInputId(), isExtern); } public CompilerInput(SourceAst ast, String inputId, boolean isExtern) { this(ast, new InputId(inputId), isExtern); } public CompilerInput(SourceAst ast, InputId inputId, boolean isExtern) { this.ast = ast; this.id = inputId; // TODO(nicksantos): Add a precondition check here. People are passing // in null, but they should not be. if (ast != null && ast.getSourceFile() != null) { ast.getSourceFile().setIsExtern(isExtern); } } public CompilerInput(SourceFile file) { this(file, false); } public CompilerInput(SourceFile file, boolean isExtern) { this(new JsAst(file), isExtern); } /** Returns a name for this input. Must be unique across all inputs. */ @Override public InputId getInputId() { return id; } /** Returns a name for this input. Must be unique across all inputs. */ @Override public String getName() { return id.getIdName(); } public SourceAst getAst() { return ast; } /** Gets the path relative to closure-base, if one is available. */ @Override public String getPathRelativeToClosureBase() { // TODO(nicksantos): Implement me. throw new UnsupportedOperationException(); } @Override public Node getAstRoot(AbstractCompiler compiler) { Node root = ast.getAstRoot(compiler); // The root maybe null if the AST can not be created. if (root != null) { Preconditions.checkState(root.isScript()); Preconditions.checkNotNull(root.getInputId()); } return root; } @Override public void clearAst() { ast.clearAst(); } @Override public SourceFile getSourceFile() { return ast.getSourceFile(); } @Override public void setSourceFile(SourceFile file) { ast.setSourceFile(file); } /** Returns the SourceAst object on

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> which this input is based. */ public SourceAst getSourceAst() { return ast; } /** Sets an abstract compiler for doing parsing. */ public void setCompiler(AbstractCompiler compiler) { this.compiler = compiler; } private void checkErrorManager() { Preconditions.checkNotNull(compiler, "Expected setCompiler to be called first: " + this); Preconditions.checkNotNull(compiler.getErrorManager(), "Expected compiler to call an error manager: " + this); } /** Gets a list of types depended on by this input. */ @Override public Collection<String> getRequires() { checkErrorManager(); try { regenerateDependencyInfoIfNecessary(); return Collections.<String>unmodifiableSet(requires); } catch (IOException e) { compiler.getErrorManager().report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.<String>of(); } } /** Gets a list of types provided by this input. */ @Override public Collection<String> getProvides() { checkErrorManager(); try { regenerateDependencyInfoIfNecessary(); return Collections.<String>unmodifiableSet(provides); } catch (IOException e) { compiler.getErrorManager().report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.<String>of(); } } // TODO(nicksantos): Remove addProvide/addRequire/removeRequire once // there is better support for discovering non-closure dependencies. void addProvide(String provide) { getProvides(); provides.add(provide); } void addRequire(String require) { getRequires(); requires.add(require); } public void removeRequire(String require) { getRequires(); requires.remove(require); } /** * Regenerates the provides/requires if we need to do so. */ private void regenerateDependencyInfoIfNecessary() throws IOException { // If the code is NOT a JsAst, then it was not originally JS code. // Look at the Ast for dependency info. if (!(ast instanceof JsAst)) { Preconditions.checkNotNull(compiler, "Expected setCompiler to be called first"); DepsFinder finder =

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Result() && !parent.isScript()) { return; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { visitSubtree(child, n); } } } /** * Gets the source line for the indicated line number. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Does not include the newline at the end * of the file. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public String getLine(int lineNumber) { return getSourceFile().getLine(lineNumber); } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public Region getRegion(int lineNumber) { return getSourceFile().getRegion(lineNumber); } public String getCode() throws IOException { return getSourceFile().getCode(); } /** Returns the module to which the input belongs. */ public JSModule getModule() { return module; } /** Sets the module to which the input belongs. */ public void setModule(JSModule module) { // An input may only belong to one module. Preconditions.checkArgument( module == null || this.module == null || this.module == module); this.module = module; } /** Overrides the module to which the input belongs. */ void overrideModule(JSModule module) { this.module = module; } public boolean isExtern() { if (ast == null || ast.getSourceFile() == null) { return false; } return ast.getSourceFile().isExtern(); } void setIsExtern(boolean isExtern) { if (ast == null || ast.getSourceFile() == null) { return; } ast.getSourceFile().

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.ParameterizedType; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.TemplateType; import com.google.javascript.rhino.jstype.UnionType; import com.google.javascript.rhino.jstype.Visitor; /** * Chainable reverse abstract interpreter providing basic functionality. * */ public abstract class ChainableReverseAbstractInterpreter implements ReverseAbstractInterpreter { protected final CodingConvention convention; final JSTypeRegistry typeRegistry; private ChainableReverseAbstractInterpreter firstLink; private ChainableReverseAbstractInterpreter nextLink; /** * Constructs an interpreter, which is the only link in a chain. Interpreters * can be appended using {@link #append}. */ public ChainableReverseAbstractInterpreter(CodingConvention convention, JSTypeRegistry typeRegistry) { Preconditions.checkNotNull(convention); this.convention = convention; this.typeRegistry = typeRegistry; firstLink = this; nextLink = null; } /** * Appends a link to {@code this}, returning the updated last link. * <p> * The pattern {@code new X().append(new Y())...append(new Z())} forms a * chain starting with X, then Y, then ... Z. * @param lastLink a chainable interpreter, with no next link * @return the updated last link */ public ChainableReverseAbstractInterpreter append( ChainableReverseAbstractInterpreter lastLink) { Preconditions.checkArgument(lastLink.nextLink == null); this.nextLink = lastLink; lastLink.firstLink = this.firstLink; return lastLink; } /** * Gets the first link of this chain. */ public ChainableReverseAbstractInterpreter getFirst() { return firstLink; } /** * Calculates the preciser scope starting with the first link. */ protected FlowScope firstPrec

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>NameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); Preconditions.checkNotNull(qualifiedName); JSType origType = node.getJSType(); origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType; scope.inferQualifiedSlot(node, qualifiedName, origType, type); break; case Token.THIS: // "this" references aren't currently modeled in the CFG. break; default: throw new IllegalArgumentException("Node cannot be refined. \n" + node.toStringTree()); } } /** * @see #getRestrictedWithoutUndefined(JSType) */ private final Visitor<JSType> restrictUndefinedVisitor = new Visitor<JSType>() { @Override public JSType caseEnumElementType(EnumElementType enumElementType) { JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().equals(type)) { return enumElementType; } else { return type; } } @Override public JSType caseAllType() { return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNullType() { return getNativeType(NULL_TYPE); } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseStringType() { return getNativeType(STRING_TYPE);

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> case '\uFEFF': // <BOM> return TernaryValue.TRUE; default: return (Character.getType(c) == Character.SPACE_SEPARATOR) ? TernaryValue.TRUE : TernaryValue.FALSE; } } /** * Gets the function's name. This method recognizes five forms: * <ul> * <li>{@code function name() ...}</li> * <li>{@code var name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * In two last cases with named function expressions, the second name is * returned (the variable of qualified name). * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getFunctionName(Node n) { Preconditions.checkState(n.isFunction()); Node parent = n.getParent(); switch (parent.getType()) { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getQualifiedName(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... String name = n.getFirstChild().getQualifiedName(); return name; } } /** * Gets the function's name. This method recognizes the forms: * <ul> * <li>{@code &#123;'name': function() ...&#125;}</li> * <li>{@code &#123;name: function() ...&#125;}</li> * <li>{@code function name() ...}</li> * <li>{@code var name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1()

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Child().isString() && STRING_REGEXP_METHODS.contains( nameNode.getLastChild().getString())) { Node param = nameNode.getNext(); if (param != null && (param.isString() || param.isRegExp())) return false; } } } return true; } /** * @return Whether the call has a local result. */ static boolean callHasLocalResult(Node n) { Preconditions.checkState(n.isCall()); return (n.getSideEffectFlags() & Node.FLAG_LOCAL_RESULTS) > 0; } /** * @return Whether the new has a local result. */ static boolean newHasLocalResult(Node n) { Preconditions.checkState(n.isNew()); return n.isOnlyModifiesThisCall(); } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { return nodeTypeMayHaveSideEffects(n, null); } static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) { if (isAssignmentOp(n)) { return true; } switch(n.getType()) { case Token.DELPROP: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.CALL: return NodeUtil.functionCallHasSideEffects(n, compiler); case Token.NEW: return NodeUtil.constructorCallHasSideEffects(n, compiler); case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set<String> emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set of names known to be constant value at * node

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set<String> knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true; case Token.NAME: // Non-constant names values may have been changed. return !isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Function expression are not changed by side-effects, // and function declarations are not part of expressions. Preconditions.checkState(isFunctionExpression(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-and & * 8 equality == != * 9 relational < <= > >= * 10 bitwise shift << >> >>> * 11 addition/subtraction + - * 12 multiply/divide * / % * 13 negation/increment ! ~ - ++ -- * 14 call, member () [] . */ static int precedence(int type) { switch (type) { case Token.COMMA: return 0; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> return Token.LSH; case Token.ASSIGN_RSH: return Token.RSH; case Token.ASSIGN_URSH: return Token.URSH; case Token.ASSIGN_ADD: return Token.ADD; case Token.ASSIGN_SUB: return Token.SUB; case Token.ASSIGN_MUL: return Token.MUL; case Token.ASSIGN_DIV: return Token.DIV; case Token.ASSIGN_MOD: return Token.MOD; } throw new IllegalArgumentException("Not an assignment op:" + n); } /** * Determines if the given node contains a function statement or function * expression. */ static boolean containsFunction(Node n) { return containsType(n, Token.FUNCTION); } /** * Returns true if the shallow scope contains references to 'this' keyword */ static boolean referencesThis(Node n) { Node start = (n.isFunction()) ? n.getLastChild() : n; return containsType(start, Token.THIS, MATCH_NOT_FUNCTION); } /** * Is this a GETPROP or GETELEM node? */ static boolean isGet(Node n) { return n.isGetProp() || n.isGetElem(); } /** * Is this node the name of a variable being declared? * * @param n The node * @return True if {@code n} is NAME and {@code parent} is VAR */ static boolean isVarDeclaration(Node n) { // There is no need to verify that parent != null because a NAME node // always has a parent in a valid parse tree. return n.isName() && n.getParent().isVar(); } /** * For an assignment or variable declaration get the assigned value. * @return The value node representing the new value. */ static Node getAssignedValue(Node n) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); if (parent.isVar()) { return n.getFirstChild(); } else if (parent.isAssign() && parent.getFirstChild() == n) { return n.getNext(); } else { return null; } } /** * Is this node an assignment expression statement? * *

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.WITH: case Token.IF: case Token.LABEL: case Token.TRY: case Token.CATCH: case Token.SWITCH: case Token.CASE: case Token.DEFAULT_CASE: return true; default: return false; } } /** * Determines whether the given node is code node for FOR, DO, * WHILE, WITH, or IF node. */ static boolean isControlStructureCodeBlock(Node parent, Node n) { switch (parent.getType()) { case Token.FOR: case Token.WHILE: case Token.LABEL: case Token.WITH: return parent.getLastChild() == n; case Token.DO: return parent.getFirstChild() == n; case Token.IF: return parent.getFirstChild() != n; case Token.TRY: return parent.getFirstChild() == n || parent.getLastChild() == n; case Token.CATCH: return parent.getLastChild() == n; case Token.SWITCH: case Token.CASE: return parent.getFirstChild() != n; case Token.DEFAULT_CASE: return true; default: Preconditions.checkState(isControlStructure(parent)); return false; } } /** * Gets the condition of an ON_TRUE / ON_FALSE CFG edge. * @param n a node with an outgoing conditional CFG edge * @return the condition node or null if the condition is not obviously a node */ static Node getConditionExpression(Node n) { switch (n.getType()) { case Token.IF: case Token.WHILE: return n.getFirstChild(); case Token.DO: return n.getLastChild(); case Token.FOR: switch (n.getChildCount()) { case 3: return null; case 4: return n.getFirstChild().getNext(); } throw new IllegalArgumentException("malformed 'for' statement " + n); case Token.CASE: return null; } throw new IllegalArgumentException(n + " does not have a condition."); } /** * @return Whether the node is of a type

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> that contain other statements. */ static boolean isStatementBlock(Node n) { return n.isScript() || n.isBlock(); } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { return isStatementParent(n.getParent()); } static boolean isStatementParent(Node parent) { // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node can be // either part of an expression or a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.isCase() || n.isDefaultCase(); } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty function expression name). */ static boolean isReferenceName(Node n) { return n.isName() && !n.getString().isEmpty(); } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent.isTry() && parent.getChildCount() == 3 && child == parent.getLastChild(); } /** Whether the node is a CATCH container BLOCK. */ static boolean isTryCatchNodeContainer(Node n) { Node parent = n.getParent(); return parent.isTry() && parent.getFirstChild().getNext() == n; } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { if (isTryFinallyNode(parent, node)) { if (NodeUtil.hasCatchHandler(getCatchBlock(parent))) { // A finally can only be removed if there is a catch. parent.removeChild(node); } else { // Otherwise, only its children can be removed. node.detachChildren(); } } else if (node.isCatch()) {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> // The CATCH can can only be removed if there is a finally clause. Node tryNode = node.getParent().getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachFromParent(); } else if (isTryCatchNodeContainer(node)) { // The container node itself can't be removed, but the contained CATCH // can if there is a 'finally' clause Node tryNode = node.getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachChildren(); } else if (node.isBlock()) { // Simply empty the block. This maintains source location and // "synthetic"-ness. node.detachChildren(); } else if (isStatementBlock(parent) || isSwitchCase(node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.isVar()) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This would leave an empty VAR, remove the VAR itself. removeChild(parent.getParent(), parent); } } else if (parent.isLabel() && node == parent.getLastChild()) { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // A LABEL without children can not be referred to, remove it. removeChild(parent.getParent(), parent); } else if (parent.isFor() && parent.getChildCount() == 4) { // Only Token.FOR can have an Token.EMPTY other control structure // need something for the condition. Others need to be replaced // or the structure removed. parent.replaceChild(node, IR.empty()); } else { throw new IllegalStateException("Invalid attempt to remove node: " + node.toString() + " of "+ parent.toString()); } } /** * Add a finally block if one does not exist. */ static void maybeAddFinally(Node tryNode) { Preconditions.checkState(tryNode.isTry()); if (!NodeUtil.hasFinally(tryNode)) { tryNode.addChildrenToBack(

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>IR.block().srcref(tryNode)); } } /** * Merge a block with its parent block. * @return Whether the block was removed. */ static boolean tryMergeBlock(Node block) { Preconditions.checkState(block.isBlock()); Node parent = block.getParent(); // Try to remove the block if its parent is a block/script or if its // parent is label and it has exactly one child. if (isStatementBlock(parent)) { Node previous = block; while (block.hasChildren()) { Node child = block.removeFirstChild(); parent.addChildAfter(child, previous); previous = child; } parent.removeChild(block); return true; } else { return false; } } /** * @param node A node * @return Whether the call is a NEW or CALL node. */ static boolean isCallOrNew(Node node) { return node.isCall() || node.isNew(); } /** * Return a BLOCK node for the given FUNCTION node. */ static Node getFunctionBody(Node fn) { Preconditions.checkArgument(fn.isFunction()); return fn.getLastChild(); } /** * Is this node or any of its children a CALL? */ static boolean containsCall(Node n) { return containsType(n, Token.CALL); } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression; see {@link #isFunctionExpression}). */ static boolean isFunctionDeclaration(Node n) { return n.isFunction() && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().isScript() || n.getParent().getParent().isFunction()); } /** * Is a FUNCTION node an function expression? An function expression is one * that has either no name or a name that is not added to

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> the current scope. * * <p>Some examples of function expressions: * <pre> * (function () {}) * (function f() {})() * [ function f() {} ] * var f = function f() {}; * for (function f() {};;) {} * </pre> * * <p>Some examples of functions that are <em>not</em> expressions: * <pre> * function f() {} * if (x); else function f() {} * for (;;) { function f() {} } * </pre> * * @param n A node * @return Whether n is an function used within an expression. */ static boolean isFunctionExpression(Node n) { return n.isFunction() && !isStatement(n); } /** * Determines if a node is a function expression that has an empty body. * * @param node a node * @return whether the given node is a function expression that is empty */ static boolean isEmptyFunctionExpression(Node node) { return isFunctionExpression(node) && isEmptyBlock(node.getLastChild()); } /** * Determines if a function takes a variable number of arguments by * looking for references to the "arguments" var_args object. */ static boolean isVarArgsFunction(Node function) { // TODO(johnlenz): rename this function Preconditions.checkArgument(function.isFunction()); return isNameReferenced( function.getLastChild(), "arguments", MATCH_NOT_FUNCTION); } /** * @return Whether node is a call to methodName. * a.f(...) * a['f'](...) */ static boolean isObjectCallMethod(Node callNode, String methodName) { if (callNode.isCall()) { Node functionIndentifyingExpression = callNode.getFirstChild(); if (isGet(functionIndentifyingExpression)) { Node last = functionIndentifyingExpression.getLastChild(); if (last != null && last.isString()) { String propName = last.getString(); return (propName.equals(methodName)); } } } return false; } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> we only do this * because it makes sense to treat this as syntactically similar to * "var x = 0;". * * @param n The node * @return True if n is an L-value. */ static boolean isLValue(Node n) { Preconditions.checkArgument(n.isName() || n.isGetProp() || n.isGetElem()); Node parent = n.getParent(); return (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) || (NodeUtil.isForIn(parent) && parent.getFirstChild() == n) || parent.isVar() || (parent.isFunction() && parent.getFirstChild() == n) || parent.isDec() || parent.isInc() || parent.isParamList() || parent.isCatch(); } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { switch (node.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Get the name of an object literal key. * * @param key A node */ static String getObjectLitKeyName(Node key) { switch (key.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return key.getString(); } throw new IllegalStateException("Unexpected node type: " + key); } /** * @param key A OBJECTLIT key node. * @return The type expected when using the key. */ static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) { if (valueType != null) { switch (key.getType()) { case Token.GETTER_DEF: // GET must always return a function type. if (valueType.isFunctionType()) { FunctionType fntype = valueType.toMaybeFunctionType(); value

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = IR.var( IR.name(nameNode.getString()) .srcref(nameNode)) .srcref(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.isBlock() || addingRoot.isScript()); Preconditions.checkState(addingRoot.getFirstChild() == null || !addingRoot.getFirstChild().isScript()); return addingRoot; } /** * Creates a node representing a qualified name. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @return A NAME or GETPROP node */ public static Node newQualifiedNameNode( CodingConvention convention, String name) { int endPos = name.indexOf('.'); if (endPos == -1) { return newName(convention, name); } Node node = newName(convention, name.substring

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(0, endPos)); int startPos; do { startPos = endPos + 1; endPos = name.indexOf('.', startPos); String part = (endPos == -1 ? name.substring(startPos) : name.substring(startPos, endPos)); Node propNode = IR.string(part); if (convention.isConstantKey(part)) { propNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } node = IR.getprop(node, propNode); } while (endPos != -1); return node; } /** * Creates a node representing a qualified name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return A NAME or GETPROP node */ static Node newQualifiedNameNode( CodingConvention convention, String name, Node basisNode, String originalName) { Node node = newQualifiedNameNode(convention, name); setDebugInformation(node, basisNode, originalName); return node; } /** * Gets the root node of a qualified name. Must be either NAME or THIS. */ public static Node getRootOfQualifiedName(Node qName) { for (Node current = qName; true; current = current.getFirstChild()) { if (current.isName() || current.isThis()) { return current; } Preconditions.checkState(current.isGetProp()); } } /** * Sets the debug information (source file info and original name) * on the given node. * * @param node The node on which to set the debug information. * @param basisNode The basis node from which to copy the source file info. * @param originalName The original name of the node. */ static void setDebugInformation(Node node, Node basisNode, String originalName) { node.copy

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> * * @return True if all characters in the string are in Basic Latin set. */ static boolean isLatin(String s) { char LARGEST_BASIC_LATIN = 0x7f; int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c > LARGEST_BASIC_LATIN) { return false; } } return true; } /** * Determines whether the given name is a valid variable name. */ public static boolean isValidSimpleName(String name) { return TokenStream.isJSIdentifier(name) && !TokenStream.isKeyword(name) && // no Unicode escaped characters - some browsers are less tolerant // of Unicode characters that might be valid according to the // language spec. // Note that by this point, Unicode escapes have been converted // to UTF-16 characters, so we're only searching for character // values, not escapes. isLatin(name); } /** * Determines whether the given name is a valid qualified name. */ // TODO(nicksantos): This should be moved into a "Language" API, // so that the results are different for es5 and es3. public static boolean isValidQualifiedName(String name) { if (name.endsWith(".") || name.startsWith(".")) { return false; } String[] parts = name.split("\\."); for (String part : parts) { if (!isValidSimpleName(part)) { return false; } } return true; } /** * Determines whether the given name can appear on the right side of * the dot operator. Many properties (like reserved words) cannot. */ static boolean isValidPropertyName(String name) { return isValidSimpleName(name); } private static class VarCollector implements Visitor { final Map<String, Node> vars = Maps.newLinkedHashMap(); @Override public void visit(Node n) { if (n.isName()) { Node parent = n.getParent(); if (parent != null && parent.isVar()) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> { Node nodeName = IR.name(name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.srcref(value); } Node var = IR.var(nodeName).srcref(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } @Override public boolean apply(Node n) { return n.isName() && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } @Override public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate<Node> { @Override public boolean apply(Node n) { return isFunctionDeclaration(n) || n.isVar(); } } /** * A predicate for matching anything except function nodes. */ private static class MatchNotFunction implements Predicate<Node>{ @Override public boolean apply(Node n) { return !n.isFunction(); } } static final Predicate<Node> MATCH_NOT_FUNCTION = new MatchNotFunction(); /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate<Node>{ @Override public boolean apply(Node n) { Node parent = n.getParent(); return n.isBlock() || (!n.isFunction() && (parent == null || isControlStructure(parent) || isStatementBlock(parent))); } } /** * Finds the number of times a type is referenced within the node tree. */ static int getNodeTypeReferenceCount( Node node, int type, Predicate<Node> traverseChildrenPred) { return getCount(node, new MatchNodeType(type), traverseChildrenPred); } /** * Whether a simple

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) { visitor.visit(node); if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPreOrder(c, visitor, traverseChildrenPred); } } } /** * A post-order traversal, calling Visitor.visit for each child matching * the predicate. */ static void visitPostOrder(Node node, Visitor visitor, Predicate<Node> traverseChildrenPred) { if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPostOrder(c, visitor, traverseChildrenPred); } } visitor.visit(node); } /** * @return Whether a TRY node has a finally block. */ static boolean hasFinally(Node n) { Preconditions.checkArgument(n.isTry()); return n.getChildCount() == 3; } /** * @return The BLOCK node containing the CATCH node (if any) * of a TRY. */ static Node getCatchBlock(Node n) { Preconditions.checkArgument(n.isTry()); return n.getFirstChild().getNext(); } /** * @return Whether BLOCK (from a TRY node) contains a CATCH. * @see NodeUtil#getCatchBlock */ static boolean hasCatchHandler(Node n) { Preconditions.checkArgument(n.isBlock()); return n.hasChildren() && n.getFirstChild().isCatch(); } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ public static Node getFunctionParameters(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.isFunction()); return fnNode.getFirstChild().getNext(); } /** * Returns true if a name node represents a constant variable. * * <p>Determining whether a variable is constant has three steps: * <ol> * <li>In CodingConventionAnnotator, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>CONSTANT_NAME property. * <li>The normalize pass renames any variable with the IS_CONSTANT_NAME * annotation and that is initialized to a constant value with * a variable name including $$constant. * <li>Return true here if the variable includes $$constant in its name. * </ol> * * @param node A NAME or STRING node * @return True if the variable is constant */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** Whether the given name is constant by coding convention. */ static boolean isConstantByConvention( CodingConvention convention, Node node, Node parent) { String name = node.getString(); if (parent.isGetProp() && node == parent.getLastChild()) { return convention.isConstantKey(name); } else if (isObjectLitKey(node, parent)) { return convention.isConstantKey(name); } else { return convention.isConstant(name); } } /** * Get the JSDocInfo for a function. */ public static JSDocInfo getFunctionJSDocInfo(Node n) { Preconditions.checkState(n.isFunction()); JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null && NodeUtil.isFunctionExpression(n)) { // Look for the info on other nodes. Node parent = n.getParent(); if (parent.isAssign()) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.isName()) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } return fnInfo; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ public static String getSourceName(Node n) { String sourceName = null; while (sourceName == null && n != null) { sourceName = n.getSourceFileName(); n = n.getParent(); } return sourceName; } /** * @param n The node. * @return The source name property on the node or

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Given the function, this returns the nth * argument or null if no such parameter exists. */ static Node getArgumentForFunction(Node function, int index) { Preconditions.checkState(function.isFunction()); return getNthSibling( function.getFirstChild().getNext().getFirstChild(), index); } /** * Given the new or call, this returns the nth * argument of the call or null if no such argument exists. */ static Node getArgumentForCallOrNew(Node call, int index) { Preconditions.checkState(isCallOrNew(call)); return getNthSibling( call.getFirstChild().getNext(), index); } private static boolean isToStringMethodCall(Node call) { Node getNode = call.getFirstChild(); if (isGet(getNode)) { Node propNode = getNode.getLastChild(); return propNode.isString() && "toString".equals(propNode.getString()); } return false; } /** Find the best JSDoc for the given node. */ static JSDocInfo getBestJSDocInfo(Node n) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { Node parent = n.getParent(); if (parent == null) { return null; } if (parent.isName()) { return getBestJSDocInfo(parent); } else if (parent.isAssign()) { return parent.getJSDocInfo(); } else if (isObjectLitKey(parent, parent.getParent())) { return parent.getJSDocInfo(); } else if (parent.isFunction()) { return parent.getJSDocInfo(); } else if (parent.isVar() && parent.hasOneChild()) { return parent.getJSDocInfo(); } else if ((parent.isHook() && parent.getFirstChild() != n) || parent.isOr() || parent.isAnd() || (parent.isComma() && parent.getFirstChild() != n)) { return getBestJSDocInfo(parent); } } return info; } /** Find the l-value that the given r-value is being assigned to. */ static Node getBestLValue(Node n) { Node parent

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>threshold) { Preconditions.checkState(Thread.currentThread() == startThread); ThreadTrace trace = getThreadTrace(); // Do nothing if the thread trace was not initialized. if (!trace.isInitialized()) { return 0; } stopTimeMs = clock.currentTimeMillis(); if (extraTracingValues != null) { // We use extraTracingValues.length rather than // extraTracingStatistics.size() because a new statistic may // have been added for (int i = 0; i < extraTracingValues.length; i++) { long value = extraTracingStatistics.get(i).stop(startThread); extraTracingValues[i] = value - extraTracingValues[i]; } } // Do nothing if the thread trace was not initialized. if (!trace.isInitialized()) { return 0; } trace.endEvent(this, silence_threshold); return stopTimeMs - startTimeMs; } /** Stop the trace using the default silence_threshold * * @return The time that this trace actually ran. */ long stop() { return stop(-1); } @Override public String toString() { if (type == null) { return comment; } else { return "[" + type + "] " + comment; } } static void setDefaultSilenceThreshold(int threshold) { getThreadTrace().defaultSilenceThreshold = threshold; } /** * Initialize the trace associated with the current thread by clearing * out any existing trace. There shouldn't be a trace so if one is * found we log it as an error. */ static void initCurrentThreadTrace() { ThreadTrace events = getThreadTrace(); if (!events.isEmpty()) { logger.log(Level.WARNING, "Non-empty timer log:\n" + events, new Throwable()); clearThreadTrace(); // Grab a new thread trace if we find a previous non-empty ThreadTrace. events = getThreadTrace(); } // Mark the thread trace as initialized. events.init(); } static void initCurrentThreadTrace(int default_silence_threshold) { initCurrentThreadTrace(); setDefaultSilenceThreshold(default_silence_threshold); } /** * Returns a timer report similar to the one

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>AlreadyOutstanding = outstandingEvents.add(t); Preconditions.checkState(notAlreadyOutstanding); } /** * Called by {@link Tracer#stop()} to create a stop event. */ void endEvent(Tracer t, int silenceThreshold) { boolean wasOutstanding = outstandingEvents.remove(t); if (!wasOutstanding) { if (isOutstandingEventsTruncated) { // The events stack overflowed and was truncated, so just log a // warning. Otherwise, we get an exception which is extremely // confusing. logger.log(Level.WARNING, "event not found, probably because the event stack " + "overflowed and was truncated", new Throwable()); } else { // throw an exception if the event was not found and the events stack // is pristine throw new IllegalStateException(); } } long elapsed = t.stopTimeMs - t.startTimeMs; if (silenceThreshold == -1) { // use default silenceThreshold = defaultSilenceThreshold; } if (elapsed < silenceThreshold) { // If this one is silent then we need to remove the start Event boolean removed = false; for (int i = 0; i < events.size(); i++) { Event e = events.get(i); if (e.tracer == t) { Preconditions.checkState(e.isStart); events.remove(i); removed = true; break; } } // Only assert if we didn't find the original and the events // weren't truncated. Preconditions.checkState(removed || isEventsTruncated); } else { events.add(new Event(false, t)); } if (t.type != null) { Stat stat = stats.get(t.type); if (stat == null) { stat = new Stat(); if (!extraTracingStatistics.isEmpty()) { stat.extraInfo = new int[extraTracingStatistics.size()]; } stats.put(t.type, stat); } stat.count++; if (typeToCountMap != null) { typeToCountMap.incrementBy(t.type, 1); } stat.clockTime += elapsed; if (typeToTimeMap != null)

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> non-controversial. #3 is * a bit trickier. It means that if you have: * <code> * /** @param {number} x / * Foo.prototype.bar = goog.abstractMethod; * </code> * the JSDocInfo will appear in two places in the type system: in the 'bar' * slot of Foo.prototype, and on the function expression type created by * this expression. * * @author nicksantos@google.com (Nick Santos) */ class InferJSDocInfo extends AbstractPostOrderCallback implements HotSwapCompilerPass { private final AbstractCompiler compiler; @SuppressWarnings("unused") private boolean inExterns; InferJSDocInfo(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { if (externs != null) { inExterns = true; NodeTraversal.traverse(compiler, externs, this); } if (root != null) { inExterns = false; NodeTraversal.traverse(compiler, root, this); } } @Override public void hotSwapScript(Node root, Node originalRoot) { Preconditions.checkNotNull(root); Preconditions.checkState(root.isScript()); inExterns = false; NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo docInfo; switch (n.getType()) { // Infer JSDocInfo on types of all type declarations on variables. case Token.NAME: if (parent == null) { return; } // Only allow JSDoc on VARs, function declarations, and assigns. if (!parent.isVar() && !NodeUtil.isFunctionDeclaration(parent) && !(parent.isAssign() && n == parent.getFirstChild())) { return; } // There are four places the doc info could live. // 1) A FUNCTION node. // /** ... */ function f() { ... } // 2) An ASSIGN parent. // /** ... */ x = function () { ... } //

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> code identical to how it's been for years. this.outputCharsetEncoder = null; } else { this.outputCharsetEncoder = outputCharset.newEncoder(); } } CodeGenerator(CodeConsumer consumer) { this(consumer, null); } /** * Insert a ECMASCRIPT 5 strict annotation. */ public void tagAsStrict() { add("'use strict';"); } void add(String str) { cc.add(str); } private void addIdentifier(String identifier) { cc.addIdentifier(identifierEscape(identifier)); } void add(Node n) { add(n, Context.OTHER); } void add(Node n, Context context) { if (!cc.continueProcessing()) { return; } int type = n.getType(); String opstr = NodeUtil.opToStr(type); int childCount = n.getChildCount(); Node first = n.getFirstChild(); Node last = n.getLastChild(); // Handle all binary operators if (opstr != null && first != last) { Preconditions.checkState( childCount == 2, "Bad binary operator \"%s\": expected 2 arguments but got %s", opstr, childCount); int p = NodeUtil.precedence(type); // For right-hand-side of operations, only pass context if it's // the IN_FOR_INIT_CLAUSE one. Context rhsContext = getContextForNoInOperator(context); // Handle associativity. // e.g. if the parse tree is a * (b * c), // we can simply generate a * b * c. if (last.getType() == type && NodeUtil.isAssociative(type)) { addExpr(first, p, context); cc.addOp(opstr, true); addExpr(last, p, rhsContext); } else if (NodeUtil.isAssignmentOp(n) && NodeUtil.isAssignmentOp(last)) { // Assignments are the only right-associative binary operators addExpr(first, p, context); cc.addOp(opstr, true); addExpr(last, p, rhsContext); } else { unrollBinaryOperator(n, type

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, opstr, context, rhsContext, p, p + 1); } return; } cc.startSourceMapping(n); switch (type) { case Token.TRY: { Preconditions.checkState(first.getNext().isBlock() && !first.getNext().hasMoreThanOneChild()); Preconditions.checkState(childCount >= 2 && childCount <= 3); add("try"); add(first, Context.PRESERVE_BLOCK); // second child contains the catch block, or nothing if there // isn't a catch block Node catchblock = first.getNext().getFirstChild(); if (catchblock != null) { add(catchblock); } if (childCount == 3) { add("finally"); add(last, Context.PRESERVE_BLOCK); } break; } case Token.CATCH: Preconditions.checkState(childCount == 2); add("catch("); add(first); add(")"); add(last, Context.PRESERVE_BLOCK); break; case Token.THROW: Preconditions.checkState(childCount == 1); add("throw"); add(first); // Must have a ';' after a throw statement, otherwise safari can't // parse this. cc.endStatement(true); break; case Token.RETURN: add("return"); if (childCount == 1) { add(first); } else { Preconditions.checkState(childCount == 0); } cc.endStatement(); break; case Token.VAR: if (first != null) { add("var "); addList(first, false, getContextForNoInOperator(context)); } break; case Token.LABEL_NAME: Preconditions.checkState(!n.getString().isEmpty()); addIdentifier(n.getString()); break; case Token.NAME: if (first == null || first.isEmpty()) { addIdentifier(n.getString()); } else { Preconditions.checkState(childCount == 1); addIdentifier(n.getString()); cc.addOp("=", true); if (first.isComma()) { addExpr(first, NodeUtil.precedence(Token.ASSIGN), Context.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>OTHER); } else { // Add expression, consider nearby code at lowest level of // precedence. addExpr(first, 0, getContextForNoInOperator(context)); } } break; case Token.ARRAYLIT: add("["); addArrayList(first); add("]"); break; case Token.PARAM_LIST: add("("); addList(first); add(")"); break; case Token.COMMA: Preconditions.checkState(childCount == 2); unrollBinaryOperator(n, Token.COMMA, ",", context, Context.OTHER, 0, 0); break; case Token.NUMBER: Preconditions.checkState(childCount == 0); cc.addNumber(n.getDouble()); break; case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: { // All of these unary operators are right-associative Preconditions.checkState(childCount == 1); cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type), Context.OTHER); break; } case Token.NEG: { Preconditions.checkState(childCount == 1); // It's important to our sanity checker that the code // we print produces the same AST as the code we parse back. // NEG is a weird case because Rhino parses "- -2" as "2". if (n.getFirstChild().isNumber()) { cc.addNumber(-n.getFirstChild().getDouble()); } else { cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type), Context.OTHER); } break; } case Token.HOOK: { Preconditions.checkState(childCount == 3); int p = NodeUtil.precedence(type); addExpr(first, p + 1, context); cc.addOp("?", true); addExpr(first.getNext(), 1, Context.OTHER); cc.addOp(":", true); addExpr(last, 1, Context.OTHER); break; } case Token

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.REGEXP: if (!first.isString() || !last.isString()) { throw new Error("Expected children to be strings"); } String regexp = regexpEscape(first.getString(), outputCharsetEncoder); // I only use one .add because whitespace matters if (childCount == 2) { add(regexp + last.getString()); } else { Preconditions.checkState(childCount == 1); add(regexp); } break; case Token.FUNCTION: if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } Preconditions.checkState(childCount == 3); boolean funcNeedsParens = (context == Context.START_OF_EXPR); if (funcNeedsParens) { add("("); } add("function"); add(first); add(first.getNext()); add(last, Context.PRESERVE_BLOCK); cc.endFunction(context == Context.STATEMENT); if (funcNeedsParens) { add(")"); } break; case Token.GETTER_DEF: case Token.SETTER_DEF: Preconditions.checkState(n.getParent().isObjectLit()); Preconditions.checkState(childCount == 1); Preconditions.checkState(first.isFunction()); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GETTER_DEF) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. if (!n.isQuotedString() && TokenStream.isJSIdentifier(name) && // do not encode literally any non-literal characters that were // Unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { //

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Determine if the string is a simple number. double d = getSimpleNumber(name); if (!Double.isNaN(d)) { cc.addNumber(d); } else { addJsString(n); } } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); } boolean preferLineBreaks = type == Token.SCRIPT || (type == Token.BLOCK && !preserveBlock && n.getParent() != null && n.getParent().isScript()); for (Node c = first; c != null; c = c.getNext()) { add(c, Context.STATEMENT); // VAR doesn't include ';' since it gets used in expressions if (c.isVar()) { cc.endStatement(); } if (c.isFunction()) { cc.maybeLineBreak(); } // Prefer to break lines in between top-level statements // because top-level statements are more homogeneous. if (preferLineBreaks) { cc.notePreferredLineBreak(); } } if (preserveBlock) { cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } break; } case Token.FOR: if (childCount == 4) { add("for("); if (first.isVar()) { add(first, Context.IN_FOR_INIT_CLAUSE); } else { addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE); } add(";"); add(first.getNext()); add(";"); add(first.getNext().getNext()); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); } else { Preconditions.checkState(childCount == 3); add("for("); add(first); add("in"); add(first.getNext()); add(")"); addNonEmptyStatement

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>( last, getContextForNonEmptyExpression(context), false); } break; case Token.DO: Preconditions.checkState(childCount == 2); add("do"); addNonEmptyStatement(first, Context.OTHER, false); add("while("); add(last); add(")"); cc.endStatement(); break; case Token.WHILE: Preconditions.checkState(childCount == 2); add("while("); add(first); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); break; case Token.EMPTY: Preconditions.checkState(childCount == 0); break; case Token.GETPROP: { Preconditions.checkState( childCount == 2, "Bad GETPROP: expected 2 children, but got %s", childCount); Preconditions.checkState( last.isString(), "Bad GETPROP: RHS should be STRING"); boolean needsParens = (first.isNumber()); if (needsParens) { add("("); } addExpr(first, NodeUtil.precedence(type), context); if (needsParens) { add(")"); } add("."); addIdentifier(last.getString()); break; } case Token.GETELEM: Preconditions.checkState( childCount == 2, "Bad GETELEM: expected 2 children but got %s", childCount); addExpr(first, NodeUtil.precedence(type), context); add("["); add(first.getNext()); add("]"); break; case Token.WITH: Preconditions.checkState(childCount == 2); add("with("); add(first); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); break; case Token.INC: case Token.DEC: { Preconditions.checkState(childCount == 1); String o = type == Token.INC ? "++" : "--"; int postProp = n.getIntProp(Node.INCRDECR_PROP); // A non-zero post-prop value indicates a post inc/dec, default of zero // is a pre-inc

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/dec. if (postProp != 0) { addExpr(first, NodeUtil.precedence(type), context); cc.addOp(o, false); } else { cc.addOp(o, false); add(first); } break; } case Token.CALL: // We have two special cases here: // 1) If the left hand side of the call is a direct reference to eval, // then it must have a DIRECT_EVAL annotation. If it does not, then // that means it was originally an indirect call to eval, and that // indirectness must be preserved. // 2) If the left hand side of the call is a property reference, // then the call must not a FREE_CALL annotation. If it does, then // that means it was originally an call without an explicit this and // that must be preserved. if (isIndirectEval(first) || n.getBooleanProp(Node.FREE_CALL) && NodeUtil.isGet(first)) { add("(0,"); addExpr(first, NodeUtil.precedence(Token.COMMA), Context.OTHER); add(")"); } else { addExpr(first, NodeUtil.precedence(type), context); } add("("); addList(first.getNext()); add(")"); break; case Token.IF: boolean hasElse = childCount == 3; boolean ambiguousElseClause = context == Context.BEFORE_DANGLING_ELSE && !hasElse; if (ambiguousElseClause) { cc.beginBlock(); } add("if("); add(first); add(")"); if (hasElse) { addNonEmptyStatement( first.getNext(), Context.BEFORE_DANGLING_ELSE, false); add("else"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); } else { addNonEmptyStatement(first.getNext(), Context.OTHER, false); Preconditions.checkState(childCount == 2); } if (ambiguousElseClause) { cc.endBlock(); } break; case Token.NULL: Preconditions.checkState(childCount == 0); cc.addConstant("null

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>"); break; case Token.THIS: Preconditions.checkState(childCount == 0); add("this"); break; case Token.FALSE: Preconditions.checkState(childCount == 0); cc.addConstant("false"); break; case Token.TRUE: Preconditions.checkState(childCount == 0); cc.addConstant("true"); break; case Token.CONTINUE: Preconditions.checkState(childCount <= 1); add("continue"); if (childCount == 1) { if (!first.isLabelName()) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.DEBUGGER: Preconditions.checkState(childCount == 0); add("debugger"); cc.endStatement(); break; case Token.BREAK: Preconditions.checkState(childCount <= 1); add("break"); if (childCount == 1) { if (!first.isLabelName()) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.EXPR_RESULT: Preconditions.checkState(childCount == 1); add(first, Context.START_OF_EXPR); cc.endStatement(); break; case Token.NEW: add("new "); int precedence = NodeUtil.precedence(type); // If the first child contains a CALL, then claim higher precedence // to force parentheses. Otherwise, when parsed, NEW will bind to the // first viable parentheses (don't traverse into functions). if (NodeUtil.containsType( first, Token.CALL, NodeUtil.MATCH_NOT_FUNCTION)) { precedence = NodeUtil.precedence(first.getType()) + 1; } addExpr(first, precedence, Context.OTHER); // '()' is optional when no arguments are present Node next = first.getNext(); if (next != null) { add("("); addList(next); add(")"); } break; case Token.STRING_KEY: Preconditions.checkState( child

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Count == 1, "Object lit key must have 1 child"); addJsString(n); break; case Token.STRING: Preconditions.checkState( childCount == 0, "A string may not have children"); addJsString(n); break; case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.isGetterDef() || c.isSetterDef()) { add(c); } else { Preconditions.checkState(c.isStringKey()); String key = c.getString(); // Object literal property names don't have to be quoted if they // are not JavaScript keywords if (!c.isQuotedString() && !TokenStream.isKeyword(key) && TokenStream.isJSIdentifier(key) && // do not encode literally any non-literal characters that // were Unicode escaped. NodeUtil.isLatin(key)) { add(key); } else { // Determine if the string is a simple number. double d = getSimpleNumber(key); if (!Double.isNaN(d)) { cc.addNumber(d); } else { addExpr(c, 1, Context.OTHER); } } add(":"); addExpr(c.getFirstChild(), 1, Context.OTHER); } } add("}"); if (needsParens) { add(")"); } break; } case Token.SWITCH: add("switch("); add(first); add(")"); cc.beginBlock(); addAllSiblings(first.getNext()); cc.endBlock(context == Context.STATEMENT); break; case Token.CASE: Preconditions.checkState(childCount == 2); add("case "); add(first); addCaseBody(last); break;

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> case Token.DEFAULT_CASE: Preconditions.checkState(childCount == 1); add("default"); addCaseBody(first); break; case Token.LABEL: Preconditions.checkState(childCount == 2); if (!first.isLabelName()) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(first); add(":"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), true); break; default: throw new Error("Unknown type " + type + "\n" + n.toStringTree()); } cc.endSourceMapping(n); } /** * We could use addList recursively here, but sometimes we produce * very deeply nested operators and run out of stack space, so we * just unroll the recursion when possible. * * We assume nodes are left-recursive. */ private void unrollBinaryOperator( Node n, int op, String opStr, Context context, Context rhsContext, int leftPrecedence, int rightPrecedence) { Node firstNonOperator = n.getFirstChild(); while (firstNonOperator.getType() == op) { firstNonOperator = firstNonOperator.getFirstChild(); } addExpr(firstNonOperator, leftPrecedence, context); Node current = firstNonOperator; do { current = current.getParent(); cc.addOp(opStr, true); addExpr(current.getFirstChild().getNext(), rightPrecedence, rhsContext); } while (current != n); } static boolean isSimpleNumber(String s) { int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c < '0' || c > '9') { return false; } } return len > 0 && s.charAt(0) != '0'; } static double getSimpleNumber(String s) { if (isSimpleNumber(s)) { try { long l = Long.parseLong(s); if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) { return l; } } catch (NumberFormatException e) { // The number was

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>} if the variable is declared as a constant, * based on the value reported by {@code NodeUtil}. */ public boolean isConst() { return nameNode != null && NodeUtil.isConstantName(nameNode); } /** * Returns {@code true} if the variable is declared as a define. * A variable is a define if it is annotated by {@code @define}. */ public boolean isDefine() { return isDefine; } public Node getInitialValue() { Node parent = getParentNode(); int pType = parent.getType(); if (pType == Token.FUNCTION) { return parent; } else if (pType == Token.ASSIGN) { return parent.getLastChild(); } else if (pType == Token.VAR) { return nameNode.getFirstChild(); } else { return null; } } /** * Gets this variable's type. To know whether this type has been inferred, * see {@code #isTypeInferred()}. */ @Override public JSType getType() { return type; } /** * Returns the name node that produced this variable. */ public Node getNameNode() { return nameNode; } /** * Gets the JSDocInfo for the variable. */ @Override public JSDocInfo getJSDocInfo() { return info; } /** * Sets this variable's type. * @throws IllegalStateException if the variable's type is not inferred */ void setType(JSType type) { Preconditions.checkState(isTypeInferred()); this.type = type; } /** * Resolve this variable's type. */ void resolveType(ErrorReporter errorReporter) { if (type != null) { type = type.resolve(errorReporter, scope); } } /** * Returns whether this variable's type is inferred. To get the variable's * type, see {@link #getType()}. */ @Override public boolean isTypeInferred() { return typeInferred; } public String getInputName() { if (input == null) return "<non-file>"; else return input.getName(); } public boolean isNoShadow() { if (info != null && info

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.isNoShadow()) { return true; } else { return false; } } @Override public boolean equals(Object other) { if (!(other instanceof Var)) { return false; } Var otherVar = (Var) other; return otherVar.nameNode == nameNode; } @Override public int hashCode() { return nameNode.hashCode(); } @Override public String toString() { return "Scope.Var " + name + "{" + type + "}"; } /** Record that this is escaped by an inner scope. */ void markEscaped() { markedEscaped = true; } /** * Whether this is escaped by an inner scope. * Notice that not all scope creators record this information. */ boolean isMarkedEscaped() { return markedEscaped; } } /** * A special subclass of Var used to distinguish "arguments" in the current * scope. */ // TODO(johnlenz): Include this the list of Vars for the scope. public static class Arguments extends Var { Arguments(Scope scope) { super( false, // no inferred "arguments", // always arguments null, // no declaration node // TODO(johnlenz): provide the type of "Arguments". null, // no type info scope, -1, // no variable index null, // input, false, // not a define null // no jsdoc ); } @Override public boolean equals(Object other) { if (!(other instanceof Arguments)) { return false; } Arguments otherVar = (Arguments) other; return otherVar.scope.getRootNode() == scope.getRootNode(); } @Override public int hashCode() { return System.identityHashCode(this); } } /** * Creates a Scope given the parent Scope and the root node of the scope. * @param parent The parent Scope. Cannot be null. * @param rootNode Typically the FUNCTION node. */ Scope(Scope parent, Node rootNode) { Preconditions.checkNotNull(parent); Preconditions.checkArgument(rootNode != parent.rootNode); this.parent = parent; this.rootNode = rootNode; JSType nodeType = rootNode

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> * @param type the variable's type * @param input the input in which this variable is defined. */ Var declare(String name, Node nameNode, JSType type, CompilerInput input) { return declare(name, nameNode, type, input, true); } /** * Declares a variable. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. * @param inferred Whether this variable's type is inferred (as opposed * to declared). */ Var declare(String name, Node nameNode, JSType type, CompilerInput input, boolean inferred) { Preconditions.checkState(name != null && name.length() > 0); // Make sure that it's declared only once Preconditions.checkState(vars.get(name) == null); // native variables do not have a name node. JSDocInfo info = nameNode == null ? null : NodeUtil.getBestJSDocInfo(nameNode); Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input, info != null && info.isDefine(), info); vars.put(name, var); return var; } /** * Undeclares a variable, to be used when the compiler optimizes out * a variable and removes it from the scope. */ void undeclare(Var var) { Preconditions.checkState(var.scope == this); Preconditions.checkState(vars.get(var.name) == var); vars.remove(var.name); } @Override public StaticSlot<JSType> getSlot(String name) { return getVar(name); } @Override public StaticSlot<JSType> getOwnSlot(String name) { return vars.get(name); } /** * Returns the variable, may be null */ public Var getVar(String name) { Var var = vars.get(name); if (var != null) { return var; } else if (parent != null) { // Recurse up the parent Scope return parent.getVar(name); }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> are duplicates of // one another. int currentIndex = 0; Iterator<JSType> it = alternates.iterator(); while (it.hasNext()) { JSType current = it.next(); // Unknown and NoResolved types may just be names that haven't // been resolved yet. So keep these in the union, and just use // equality checking for simple de-duping. if (alternate.isUnknownType() || current.isUnknownType() || alternate.isNoResolvedType() || current.isNoResolvedType() || alternate.hasAnyTemplate() || current.hasAnyTemplate()) { if (alternate.isEquivalentTo(current)) { // Alternate is unnecessary. return this; } } else { if (alternate.isSubtype(current)) { // Alternate is unnecessary. return this; } else if (current.isSubtype(alternate)) { // Alternate makes current obsolete it.remove(); if (currentIndex == functionTypePosition) { functionTypePosition = -1; } else if (currentIndex < functionTypePosition) { functionTypePosition--; currentIndex--; } } } currentIndex++; } if (alternate.isFunctionType()) { // See the comments on functionTypePosition above. Preconditions.checkState(functionTypePosition == -1); functionTypePosition = alternates.size(); } alternates.add(alternate); result = null; // invalidate the memoized result } } else { result = null; } return this; } /** * Reduce the alternates into a non-union type. * If the alternates can't be accurately represented with a non-union * type, return null. */ private JSType reduceAlternatesWithoutUnion() { if (isAllType) { return registry.getNativeType(ALL_TYPE); } else if (isNativeUnknownType) { if (areAllUnknownsChecked) { return registry.getNativeType(CHECKED_UNKNOWN_TYPE); } else { return registry.getNativeType(UNKNOWN_TYPE); } } else { int size = alternates.size(); if (size > maxUnionSize) { return registry.getNativeType(UNKNOWN_TYPE); } else

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Preconditions.checkNotNull(registerFunction); return this.registerFunction == registerFunction; } boolean isGetterFunction() { return registerFunction != null; } String getName() { return name; } String getExpectedTypeName() { return expectedTypeName; } Node createDefaultValueNode() { switch (this) { case REGISTER_BOOLEAN: return IR.falseNode(); case REGISTER_NUMBER: return IR.number(0); case REGISTER_STRING: return IR.string(""); } throw new IllegalStateException(); } } // A map of function name -> TweakFunction. private static final Map<String, TweakFunction> TWEAK_FUNCTIONS_MAP; static { TWEAK_FUNCTIONS_MAP = Maps.newHashMap(); for (TweakFunction func : TweakFunction.values()) { TWEAK_FUNCTIONS_MAP.put(func.getName(), func); } } ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks, Map<String, Node> compilerDefaultValueOverrides) { this.compiler = compiler; this.stripTweaks = stripTweaks; // Having the map sorted is required for the unit tests to be deterministic. this.compilerDefaultValueOverrides = Maps.newTreeMap(); this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides); } @Override public void process(Node externs, Node root) { CollectTweaksResult result = collectTweaks(root); applyCompilerDefaultValueOverrides(result.tweakInfos); boolean changed = false; if (stripTweaks) { changed = stripAllCalls(result.tweakInfos); } else if (!compilerDefaultValueOverrides.isEmpty()) { changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls); } if (changed) { compiler.reportCodeChange(); } } /** * Passes the compiler default value overrides to the JS by replacing calls * to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value; */ private boolean replaceGetCompilerOverridesCalls( List<TweakFunctionCall> calls) { for (TweakFunctionCall call : calls) { Node callNode = call.callNode; Node obj

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(String sourceName, TweakFunction tweakFunc, Node callNode) { functionCalls.add(new TweakFunctionCall(sourceName, tweakFunc, callNode)); } boolean isRegistered() { return registerCall != null; } Node getDefaultValueNode() { Preconditions.checkState(isRegistered()); // Use calls to goog.tweak.overrideDefaultValue() first. if (defaultValueNode != null) { return defaultValueNode; } // Use the value passed to the register function next. if (registerCall.valueNode != null) { return registerCall.valueNode; } // Otherwise, use the default value for the tweak's type. return registerCall.tweakFunc.createDefaultValueNode(); } } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> { Preconditions.checkNotNull(errorRoot); this.fnName = fnName == null ? "" : fnName; this.codingConvention = compiler.getCodingConvention(); this.typeRegistry = compiler.getTypeRegistry(); this.errorRoot = errorRoot; this.sourceName = sourceName; this.compiler = compiler; this.scope = scope; } /** * Sets the contents of this function. */ FunctionTypeBuilder setContents(@Nullable FunctionContents contents) { if (contents != null) { this.contents = contents; } return this; } /** * Infer the parameter and return types of a function from * the parameter and return types of the function it is overriding. * * @param oldType The function being overridden. Does nothing if this is null. * @param paramsParent The LP node of the function that we're assigning to. * If null, that just means we're not initializing this to a function * literal. */ FunctionTypeBuilder inferFromOverriddenFunction( @Nullable FunctionType oldType, @Nullable Node paramsParent) { if (oldType == null) { return this; } returnType = oldType.getReturnType(); returnTypeInferred = oldType.isReturnTypeInferred(); if (paramsParent == null) { // Not a function literal. parametersNode = oldType.getParametersNode(); if (parametersNode == null) { parametersNode = new FunctionParamBuilder(typeRegistry).build(); } } else { // We're overriding with a function literal. Apply type information // to each parameter of the literal. FunctionParamBuilder paramBuilder = new FunctionParamBuilder(typeRegistry); Iterator<Node> oldParams = oldType.getParameters().iterator(); boolean warnedAboutArgList = false; boolean oldParamsListHitOptArgs = false; for (Node currentParam = paramsParent.getFirstChild(); currentParam != null; currentParam = currentParam.getNext()) { if (oldParams.hasNext()) { Node oldParam = oldParams.next(); Node newParam = paramBuilder.newParameterFromNode(oldParam); oldParamsListHitOptArgs = oldParamsListHitOptArgs || oldParam.isVarArgs() || oldParam.isOptionalArg();

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> the super type resolves. * @param objectType * @return true if objectType is resolvable in the future */ private static boolean hasMoreTagsToResolve(ObjectType objectType) { Preconditions.checkArgument(objectType.isUnknownType()); if (objectType.getImplicitPrototype() != null) { // constructor extends class if (objectType.getImplicitPrototype().isResolved()) { return false; } else { return true; } } else { // interface extends interfaces FunctionType ctor = objectType.getConstructor(); if (ctor != null) { for (ObjectType interfaceType : ctor.getExtendedInterfaces()) { if (!interfaceType.isResolved()) { return true; } } } return false; } } /** Holds data dynamically inferred about functions. */ static interface FunctionContents { /** Returns the source node of this function. May be null. */ Node getSourceNode(); /** Returns if the function may be in externs. */ boolean mayBeFromExterns(); /** Returns if a return of a real value (not undefined) appears. */ boolean mayHaveNonEmptyReturns(); /** Returns if this consists of a single throw. */ boolean mayHaveSingleThrow(); /** Gets a list of variables in this scope that are escaped. */ Iterable<String> getEscapedVarNames(); /** Gets a list of variables whose properties are escaped. */ Set<String> getEscapedQualifiedNames(); } static class UnknownFunctionContents implements FunctionContents { private static UnknownFunctionContents singleton = new UnknownFunctionContents(); static FunctionContents get() { return singleton; } @Override public Node getSourceNode() { return null; } @Override public boolean mayBeFromExterns() { return true; } @Override public boolean mayHaveNonEmptyReturns() { return true; } @Override public boolean mayHaveSingleThrow() { return true; } @Override public Iterable<String> getEscapedVarNames() { return ImmutableList.of(); } @Override public Set<String> getEscapedQualifiedNames() { return ImmutableSet.of(); } } static class AstFunctionContents implements FunctionContents { private final Node n; private boolean

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> /** * Creates a pass to check global name references at the given warning level. */ CheckGlobalNames(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.convention = compiler.getCodingConvention(); this.level = level; } /** * Injects a pre-computed global namespace, so that the same namespace * can be re-used for multiple check passes. Returns this for easy chaining. */ CheckGlobalNames injectNamespace(GlobalNamespace namespace) { Preconditions.checkArgument(namespace.hasExternsRoot()); this.namespace = namespace; return this; } @Override public void process(Node externs, Node root) { if (namespace == null) { namespace = new GlobalNamespace(compiler, externs, root); } // Find prototype properties that will affect our analysis. Preconditions.checkState(namespace.hasExternsRoot()); findPrototypeProps("Object", objectPrototypeProps); findPrototypeProps("Function", functionPrototypeProps); objectPrototypeProps.addAll( convention.getIndirectlyDeclaredProperties()); for (Name name : namespace.getNameForest()) { // Skip extern names. Externs are often not runnable as real code, // and will do things like: // var x; // x.method; // which this check forbids. if (name.inExterns) { continue; } checkDescendantNames(name, name.globalSets + name.localSets > 0); } } private void findPrototypeProps(String type, Set<String> props) { Name slot = namespace.getSlot(type); if (slot != null) { for (Ref ref : slot.getRefs()) { if (ref.type == Ref.Type.PROTOTYPE_GET) { Node fullName = ref.getNode().getParent().getParent(); if (fullName.isGetProp()) { props.add(fullName.getLastChild().getString()); } } } } } /** * Checks to make sure all the descendants of a name are defined if they * are referenced. * * @param name A global name. * @param nameIsDefined If true, {@code name} is defined. Otherwise, it's * undefined, and any

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>".equals(callName.getQualifiedName()) && n.getChildCount() >= 3) { Node typeArray = callName.getNext().getNext(); if (typeArray.isArrayLit()) { List<String> typeNames = Lists.newArrayList(); for (Node name = typeArray.getFirstChild(); name != null; name = name.getNext()) { if (name.isString()) { typeNames.add(name.getString()); } } return typeNames; } } return super.identifyTypeDeclarationCall(n); } @Override public String getAbstractMethodName() { return "goog.abstractMethod"; } @Override public String getSingletonGetterClassName(Node callNode) { Node callArg = callNode.getFirstChild(); String callName = callArg.getQualifiedName(); // Use both the original name and the post-CollapseProperties name. if (!("goog.addSingletonGetter".equals(callName) || "goog$addSingletonGetter".equals(callName)) || callNode.getChildCount() != 2) { return super.getSingletonGetterClassName(callNode); } return callArg.getNext().getQualifiedName(); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { super.applySingletonGetter(functionType, getterType, objectType); functionType.defineDeclaredProperty("getInstance", getterType, functionType.getSource()); functionType.defineDeclaredProperty("instance_", objectType, functionType.getSource()); } @Override public String getGlobalObject() { return "goog.global"; } private final Set<String> propertyTestFunctions = ImmutableSet.of( "goog.isDef", "goog.isNull", "goog.isDefAndNotNull", "goog.isString", "goog.isNumber", "goog.isBoolean", "goog.isFunction", "goog.isArray", "goog.isObject"); @Override public boolean isPropertyTestFunction(Node call) { Preconditions.checkArgument(call.isCall()); return propertyTestFunctions.contains( call.getFirstChild().getQualifiedName()) || super.isPropertyTestFunction(call); } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Preconditions.checkArgument(callNode.isCall()); ObjectLiteralCast proxyCast = super.getObjectLiteralCast(callNode); if (proxyCast != null) { return proxyCast; } Node callName = callNode.getFirstChild(); if (!"goog.reflect.object".equals(callName.getQualifiedName()) || callNode.getChildCount() != 3) { return null; } Node typeNode = callName.getNext(); if (!typeNode.isQualifiedName()) { return null; } Node objectNode = typeNode.getNext(); if (!objectNode.isObjectLit()) { return new ObjectLiteralCast(null, null, OBJECTLIT_EXPECTED); } return new ObjectLiteralCast( typeNode.getQualifiedName(), typeNode.getNext(), null); } @Override public boolean isOptionalParameter(Node parameter) { return false; } @Override public boolean isVarArgsParameter(Node parameter) { return false; } @Override public boolean isPrivate(String name) { return false; } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return ImmutableList.<AssertionFunctionSpec>of( new AssertionFunctionSpec("goog.asserts.assert"), new AssertionFunctionSpec("goog.asserts.assertNumber", JSTypeNative.NUMBER_TYPE), new AssertionFunctionSpec("goog.asserts.assertString", JSTypeNative.STRING_TYPE), new AssertionFunctionSpec("goog.asserts.assertFunction", JSTypeNative.FUNCTION_INSTANCE_TYPE), new AssertionFunctionSpec("goog.asserts.assertObject", JSTypeNative.OBJECT_TYPE), new AssertionFunctionSpec("goog.asserts.assertArray", JSTypeNative.ARRAY_TYPE), new AssertInstanceofSpec("goog.asserts.assertInstanceof") ); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { Bind result = super.describeFunctionBind(n, useTypeInfo); if (result != null) { return result; } if (!n.isCall()) { return null; } Node callTarget = n.getFirstChild(); String name = callTarget.getQualifiedName(); if (name != null) { if (name.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>PropListItem { private static final long serialVersionUID = 1L; private final Object objectValue; ObjectPropListItem(int propType, Object objectValue, PropListItem next) { super(propType, next); this.objectValue = objectValue; } @Override public int getIntValue() { throw new UnsupportedOperationException(); } @Override public Object getObjectValue() { return objectValue; } @Override public String toString() { return objectValue == null ? "null" : objectValue.toString(); } @Override public PropListItem chain(PropListItem next) { return new ObjectPropListItem(getType(), objectValue, next); } } // A base class for int storing props private static class IntPropListItem extends AbstractPropListItem { private static final long serialVersionUID = 1L; final int intValue; IntPropListItem(int propType, int intValue, PropListItem next) { super(propType, next); this.intValue = intValue; } @Override public int getIntValue() { return intValue; } @Override public Object getObjectValue() { throw new UnsupportedOperationException(); } @Override public String toString() { return String.valueOf(intValue); } @Override public PropListItem chain(PropListItem next) { return new IntPropListItem(getType(), intValue, next); } } public Node(int nodeType) { type = nodeType; parent = null; sourcePosition = -1; } public Node(int nodeType, Node child) { Preconditions.checkArgument(child.parent == null, "new child has existing parent"); Preconditions.checkArgument(child.next == null, "new child has existing sibling"); type = nodeType; parent = null; first = last = child; child.next = null; child.parent = this; sourcePosition = -1; } public Node(int nodeType, Node left, Node right) { Preconditions.checkArgument(left.parent == null, "first new child has existing parent"); Preconditions.checkArgument(left.next == null, "first new child has existing sibling"); Preconditions.checkArgument(right.parent == null, "second new child has

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> existing parent"); Preconditions.checkArgument(right.next == null, "second new child has existing sibling"); type = nodeType; parent = null; first = left; last = right; left.next = right; left.parent = this; right.next = null; right.parent = this; sourcePosition = -1; } public Node(int nodeType, Node left, Node mid, Node right) { Preconditions.checkArgument(left.parent == null); Preconditions.checkArgument(left.next == null); Preconditions.checkArgument(mid.parent == null); Preconditions.checkArgument(mid.next == null); Preconditions.checkArgument(right.parent == null); Preconditions.checkArgument(right.next == null); type = nodeType; parent = null; first = left; last = right; left.next = mid; left.parent = this; mid.next = right; mid.parent = this; right.next = null; right.parent = this; sourcePosition = -1; } public Node(int nodeType, Node left, Node mid, Node mid2, Node right) { Preconditions.checkArgument(left.parent == null); Preconditions.checkArgument(left.next == null); Preconditions.checkArgument(mid.parent == null); Preconditions.checkArgument(mid.next == null); Preconditions.checkArgument(mid2.parent == null); Preconditions.checkArgument(mid2.next == null); Preconditions.checkArgument(right.parent == null); Preconditions.checkArgument(right.next == null); type = nodeType; parent = null; first = left; last = right; left.next = mid; left.parent = this; mid.next = mid2; mid.parent = this; mid2.next = right; mid2.parent = this; right.next = null; right.parent = this; sourcePosition = -1; } public Node(int nodeType, int lineno, int charno) { type = nodeType; parent = null; sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node child, int lineno

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, int charno) { this(nodeType, child); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node right, int lineno, int charno) { this(nodeType, left, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node mid, Node right, int lineno, int charno) { this(nodeType, left, mid, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node mid, Node mid2, Node right, int lineno, int charno) { this(nodeType, left, mid, mid2, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children, int lineno, int charno) { this(nodeType, children); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children) { this.type = nodeType; parent = null; if (children.length != 0) { this.first = children[0]; this.last = children[children.length - 1]; for (int i = 1; i < children.length; i++) { if (null != children[i - 1].next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } children[i - 1].next = children[i]; Preconditions.checkArgument(children[i - 1].parent == null); children[i - 1].parent = this; } Preconditions.checkArgument(children[children.length - 1].parent == null); children[children.length - 1].parent = this; if (null != this.last.next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } } } public static Node newNumber(double number) { return new NumberNode(number); } public static Node newNumber(double number, int lineno, int charno) { return new NumberNode

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(number, lineno, charno); } public static Node newString(String str) { return new StringNode(Token.STRING, str); } public static Node newString(int type, String str) { return new StringNode(type, str); } public static Node newString(String str, int lineno, int charno) { return new StringNode(Token.STRING, str, lineno, charno); } public static Node newString(int type, String str, int lineno, int charno) { return new StringNode(type, str, lineno, charno); } public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean hasChildren() { return first != null; } public Node getFirstChild() { return first; } public Node getLastChild() { return last; } public Node getNext() { return next; } public Node getChildBefore(Node child) { if (child == first) { return null; } Node n = first; while (n.next != child) { n = n.next; if (n == null) { throw new RuntimeException("node is not a child"); } } return n; } public Node getChildAtIndex(int i) { Node n = first; while (i > 0) { n = n.next; i--; } return n; } public int getIndexOfChild(Node child) { Node n = first; int i = 0; while (n != null) { if (child == n) { return i; } n = n.next; i++; } return -1; } public Node getLastSibling() { Node n = this; while (n.next != null) { n = n.next; } return n; } public void addChildToFront(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = first; first = child; if (last == null) {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> last = child; } } public void addChildToBack(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = null; if (last == null) { first = last = child; return; } last.next = child; last = child; } public void addChildrenToFront(Node children) { for (Node child = children; child != null; child = child.next) { Preconditions.checkArgument(child.parent == null); child.parent = this; } Node lastSib = children.getLastSibling(); lastSib.next = first; first = children; if (last == null) { last = lastSib; } } public void addChildrenToBack(Node children) { addChildrenAfter(children, getLastChild()); } /** * Add 'child' before 'node'. */ public void addChildBefore(Node newChild, Node node) { Preconditions.checkArgument(node != null && node.parent == this, "The existing child node of the parent should not be null."); Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); if (first == node) { newChild.parent = this; newChild.next = first; first = newChild; return; } Node prev = getChildBefore(node); addChildAfter(newChild, prev); } /** * Add 'child' after 'node'. */ public void addChildAfter(Node newChild, Node node) { Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); addChildrenAfter(newChild, node); } /** * Add all children after 'node'. */ public void addChildrenAfter(Node children, Node node) { Preconditions.checkArgument(node == null || node.parent == this); for (Node child = children; child != null; child = child.next) { Preconditions.checkArgument(child.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>parent == null); child.parent = this; } Node lastSibling = children.getLastSibling(); if (node != null) { Node oldNext = node.next; node.next = children; lastSibling.next = oldNext; if (node == last) { last = lastSibling; } } else { // Append to the beginning. if (first != null) { lastSibling.next = first; } else { last = lastSibling; } first = children; } } /** * Detach a child from its parent and siblings. */ public void removeChild(Node child) { Node prev = getChildBefore(child); if (prev == null) first = first.next; else prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; } /** * Detaches child from Node and replaces it with newChild. */ public void replaceChild(Node child, Node newChild) { Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(child); newChild.next = child.next; newChild.parent = this; if (child == first) { first = newChild; } else { Node prev = getChildBefore(child); prev.next = newChild; } if (child == last) last = newChild; child.next = null; child.parent = null; } public void replaceChildAfter(Node prevChild, Node newChild) { Preconditions.checkArgument(prevChild.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(prevChild); Node child = prevChild.next; newChild.next

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> = child.next; newChild.parent = this; prevChild.next = newChild; if (child == last) last = newChild; child.next = null; child.parent = null; } @VisibleForTesting PropListItem lookupProperty(int propType) { PropListItem x = propListHead; while (x != null && propType != x.getType()) { x = x.getNext(); } return x; } /** * Clone the properties from the provided node without copying * the property object. The receiving node may not have any * existing properties. * @param other The node to clone properties from. * @return this node. */ public Node clonePropsFrom(Node other) { Preconditions.checkState(this.propListHead == null, "Node has existing properties."); this.propListHead = other.propListHead; return this; } public void removeProp(int propType) { PropListItem result = removeProp(propListHead, propType); if (result != propListHead) { propListHead = result; } } /** * @param item The item to inspect * @param propType The property to look for * @return The replacement list if the property was removed, or * 'item' otherwise. */ private PropListItem removeProp(PropListItem item, int propType) { if (item == null) { return null; } else if (item.getType() == propType) { return item.getNext(); } else { PropListItem result = removeProp(item.getNext(), propType); if (result != item.getNext()) { return item.chain(result); } else { return item; } } } public Object getProp(int propType) { if (propType == SOURCENAME_PROP) { return getSourceFileName(); } PropListItem item = lookupProperty(propType); if (item == null) { return null; } return item.getObjectValue(); } public boolean getBooleanProp(int propType) { return getIntProp(propType) != 0; } /** * Returns the integer value for the property, or

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> * for (Node sibling : n.siblings()) { ...</pre> */ public Iterable<Node> siblings() { return new SiblingNodeIterable(this); } /** * @see Node#siblings() */ private static final class SiblingNodeIterable implements Iterable<Node>, Iterator<Node> { private final Node start; private Node current; private boolean used; SiblingNodeIterable(Node start) { this.start = start; this.current = start; this.used = false; } @Override public Iterator<Node> iterator() { if (!used) { used = true; return this; } else { // We have already used the current object as an iterator; // we must create a new SiblingNodeIterable based on this // iterable's start node. // // Since the primary use case for Node.children is in for // loops, this branch is extremely unlikely. return (new SiblingNodeIterable(start)).iterator(); } } @Override public boolean hasNext() { return current != null; } @Override public Node next() { if (current == null) { throw new NoSuchElementException(); } try { return current; } finally { current = current.getNext(); } } @Override public void remove() { throw new UnsupportedOperationException(); } } // ========================================================================== // Accessors PropListItem getPropListHeadForTesting() { return propListHead; } public Node getParent() { return parent; } /** * Gets the ancestor node relative to this. * * @param level 0 = this, 1 = the parent, etc. */ public Node getAncestor(int level) { Preconditions.checkArgument(level >= 0); Node node = this; while (node != null && level-- > 0) { node = node.getParent(); } return node; } /** * Iterates all of the node's ancestors excluding itself. */ public AncestorIterable getAncestors() { return new AncestorIterable(this.getParent()); } /** * Iterator to go up the ancestor tree. */ public static class AncestorIterable implements Iterable<Node> { private

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { String name = getString(); return name.isEmpty() ? null : name; } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such as * <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName() { switch (getType()) { case Token.NAME: return getString().isEmpty() ? false : true; case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name without * a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code> * . */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return getString().isEmpty() ? false : true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } // ========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null)

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (Node child = first; child != null;) { Node nextChild = child.getNext(); child.parent = null; child.next = null; child = nextChild; } first = null; last = null; } public Node removeChildAfter(Node prev) { Preconditions.checkArgument(prev.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(prev.next != null, "no next sibling."); Node child = prev.next; prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; return child; } /** * @return A detached clone of the Node, specifically excluding its children. */ public Node cloneNode() { Node result; try { result = (Node) super.clone(); // PropListItem lists are immutable and can be shared so there is no // need to clone them here. result.next = null; result.first = null; result.last = null; result.parent = null; } catch (CloneNotSupportedException e) { throw new RuntimeException(e.getMessage()); } return result; } /** * @return A detached clone of the Node and all its children. */ public Node cloneTree() { Node result = cloneNode(); for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) { Node n2clone = n2.cloneTree(); n2clone.parent = result; if (result.last != null) { result.last.next = n2clone; } if (result.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>1; final public static int FLAG_THIS_UNMODIFIED = 2; final public static int FLAG_ARGUMENTS_UNMODIFIED = 4; final public static int FLAG_NO_THROWS = 8; final public static int FLAG_LOCAL_RESULTS = 16; final public static int SIDE_EFFECTS_FLAGS_MASK = 31; final public static int SIDE_EFFECTS_ALL = 0; final public static int NO_SIDE_EFFECTS = FLAG_GLOBAL_STATE_UNMODIFIED | FLAG_THIS_UNMODIFIED | FLAG_ARGUMENTS_UNMODIFIED | FLAG_NO_THROWS; /** * Marks this function or constructor call's side effect flags. * This property is only meaningful for {@link Token#CALL} and * {@link Token#NEW} nodes. */ public void setSideEffectFlags(int flags) { Preconditions.checkArgument( getType() == Token.CALL || getType() == Token.NEW, "setIsNoSideEffectsCall only supports CALL and NEW nodes, got " + Token.name(getType())); putIntProp(SIDE_EFFECT_FLAGS, flags); } public void setSideEffectFlags(SideEffectFlags flags) { setSideEffectFlags(flags.valueOf()); } /** * Returns the side effects flags for this node. */ public int getSideEffectFlags() { return getIntProp(SIDE_EFFECT_FLAGS); } /** * A helper class for getting and setting the side-effect flags. * @author johnlenz@google.com (John Lenz) */ public static class SideEffectFlags { private int value = Node.SIDE_EFFECTS_ALL; public SideEffectFlags() { } public SideEffectFlags(int value) { this.value = value; } public int valueOf() { return value; } /** All side-effect occur and the returned results are non-local. */ public void setAllFlags() { value = Node.SIDE_EFFECTS_ALL; } /** No side-effects occur and the returned results are local. */ public void clearAllFlags() { value = Node.NO_SIDE_EFFECTS | Node.FLAG_LOCAL_RESULTS

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> false); } InstanceObjectType(JSTypeRegistry registry, FunctionType constructor, boolean isNativeType) { super(registry, null, null, isNativeType); Preconditions.checkNotNull(constructor); this.constructor = constructor; } @Override public String getReferenceName() { return getConstructor().getReferenceName(); } @Override public boolean hasReferenceName() { return getConstructor().hasReferenceName(); } @Override public ObjectType getImplicitPrototype() { return getConstructor().getPrototype(); } @Override public FunctionType getConstructor() { return constructor; } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { ObjectType proto = getImplicitPrototype(); if (proto != null && proto.hasOwnDeclaredProperty(name)) { return false; } return super.defineProperty(name, type, inferred, propertyNode); } @Override String toStringHelper(boolean forAnnotations) { if (constructor.hasReferenceName()) { return constructor.getReferenceName(); } else { return super.toStringHelper(forAnnotations); } } @Override boolean isTheObjectType() { return getConstructor().isNativeObjectType() && "Object".equals(getReferenceName()); } @Override public boolean isInstanceType() { return true; } @Override public boolean isArrayType() { return getConstructor().isNativeObjectType() && "Array".equals(getReferenceName()); } @Override public boolean isStringObjectType() { return getConstructor().isNativeObjectType() && "String".equals(getReferenceName()); } @Override public boolean isBooleanObjectType() { return getConstructor().isNativeObjectType() && "Boolean".equals(getReferenceName()); } @Override public boolean isNumberObjectType() { return getConstructor().isNativeObjectType() && "Number".equals(getReferenceName()); } @Override public boolean isDateType() { return getConstructor().isNativeObjectType() && "Date".equals(getReferenceName()); } @Override public boolean isRegexpType() { return getConstructor().isNativeObjectType() && "RegExp".equals(getReferenceName()); } @Override public boolean isNominalType() { return hasReferenceName

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> public static DiagnosticGroup CHECK_TYPES = DiagnosticGroups.registerGroup("checkTypes", TypeValidator.ALL_DIAGNOSTICS, TypeCheck.ALL_DIAGNOSTICS); public static DiagnosticGroup CHECK_VARIABLES = DiagnosticGroups.registerGroup("checkVars", VarCheck.UNDEFINED_VAR_ERROR, SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); public static DiagnosticGroup CHECK_USELESS_CODE = DiagnosticGroups.registerGroup("uselessCode", CheckSideEffects.USELESS_CODE_ERROR, CheckUnreachableCode.UNREACHABLE_CODE); public static DiagnosticGroup CONST = DiagnosticGroups.registerGroup("const", CheckAccessControls.CONST_PROPERTY_DELETED, CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE, ConstCheck.CONST_REASSIGNED_VALUE_ERROR); public static DiagnosticGroup TYPE_INVALIDATION = DiagnosticGroups.registerGroup("typeInvalidation", DisambiguateProperties.Warnings.INVALIDATION); public static DiagnosticGroup DUPLICATE_VARS = DiagnosticGroups.registerGroup("duplicate", SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR, TypeValidator.DUP_VAR_DECLARATION); public static DiagnosticGroup ES5_STRICT = DiagnosticGroups.registerGroup("es5Strict", ControlStructureCheck.USE_OF_WITH, StrictModeCheck.UNKNOWN_VARIABLE, StrictModeCheck.EVAL_DECLARATION, StrictModeCheck.EVAL_ASSIGNMENT, StrictModeCheck.ARGUMENTS_DECLARATION, StrictModeCheck.ARGUMENTS_ASSIGNMENT, StrictModeCheck.DELETE_VARIABLE, StrictModeCheck.DUPLICATE_OBJECT_KEY); public static DiagnosticGroup CHECK_PROVIDES = DiagnosticGroups.registerGroup("checkProvides", CheckProvides.MISSING_PROVIDE_WARNING); public static DiagnosticGroup DUPLICATE_MESSAGE = DiagnosticGroups.registerGroup("duplicateMessage", JsMessageVisitor.MESSAGE_DUPLICATE_KEY); /** * Adds warning levels by name. */ void setWarningLevel(CompilerOptions options, String name, CheckLevel level) { DiagnosticGroup group = forName(name); Preconditions.checkNotNull(group, "No warning class for name: %s", name); options.set

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> * must also look up this map and connect: * foo() -> bar() * bar() -> END */ private final Multimap<Node, Node> finallyMap = HashMultimap.create(); /** * Constructor. * * @param compiler Compiler instance. * @param shouldTraverseFunctions Whether functions should be traversed (true * by default). * @param edgeAnnotations Whether to allow edge annotations. By default, * only node annotations are allowed. */ ControlFlowAnalysis(AbstractCompiler compiler, boolean shouldTraverseFunctions, boolean edgeAnnotations) { this.compiler = compiler; this.shouldTraverseFunctions = shouldTraverseFunctions; this.edgeAnnotations = edgeAnnotations; } ControlFlowGraph<Node> getCfg() { return cfg; } @Override public void process(Node externs, Node root) { this.root = root; astPositionCounter = 0; astPosition = Maps.newHashMap(); nodePriorities = Maps.newHashMap(); cfg = new AstControlFlowGraph(computeFallThrough(root), nodePriorities, edgeAnnotations); NodeTraversal.traverse(compiler, root, this); astPosition.put(null, ++astPositionCounter); // the implicit return is last. // Now, generate the priority of nodes by doing a depth-first // search on the CFG. priorityCounter = 0; DiGraphNode<Node, Branch> entry = cfg.getEntry(); prioritizeFromEntryNode(entry); if (shouldTraverseFunctions) { // If we're traversing inner functions, we need to rank the // priority of them too. for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) { Node value = candidate.getValue(); if (value != null && value.isFunction()) { Preconditions.checkState( !nodePriorities.containsKey(candidate) || candidate == entry); prioritizeFromEntryNode(candidate); } } } // At this point, all reachable nodes have been given a priority, but // unreachable nodes have not been given a priority. Put them last. // Presumably, it doesn't really matter what priority they get, since // this shouldn't happen in real code. for (Di

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> each expression node. * * For example: within a Token.SWITCH, the expression in question does not * change the control flow and need not to be considered. */ if (parent != null) { switch (parent.getType()) { case Token.FOR: // Only traverse the body of the for loop. return n == parent.getLastChild(); // Skip the conditions. case Token.IF: case Token.WHILE: case Token.WITH: return n != parent.getFirstChild(); case Token.DO: return n != parent.getFirstChild().getNext(); // Only traverse the body of the cases case Token.SWITCH: case Token.CASE: case Token.CATCH: case Token.LABEL: return n != parent.getFirstChild(); case Token.FUNCTION: return n == parent.getFirstChild().getNext().getNext(); case Token.CONTINUE: case Token.BREAK: case Token.EXPR_RESULT: case Token.VAR: case Token.RETURN: case Token.THROW: return false; case Token.TRY: /* Just before we are about to visit the second child of the TRY node, * we know that we will be visiting either the CATCH or the FINALLY. * In other words, we know that the post order traversal of the TRY * block has been finished, no more exceptions can be caught by the * handler at this TRY block and should be taken out of the stack. */ if (n == parent.getFirstChild().getNext()) { Preconditions.checkState(exceptionHandler.peek() == parent); exceptionHandler.pop(); } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.IF: handleIf(n); return; case Token.WHILE: handleWhile(n); return; case Token.DO: handleDo(n); return; case Token.FOR: handleFor(n); return; case Token.SWITCH: handleSwitch(n); return; case Token.CASE: handleCase(n); return; case Token.DEFAULT_CASE: handleDefault(n); return; case

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> least one CASE or EMPTY createEdge(node, Branch.UNCOND, next); } else { // Has no CASE but possibly a DEFAULT if (node.getFirstChild().getNext() != null) { createEdge(node, Branch.UNCOND, node.getFirstChild().getNext()); } else { // No CASE, no DEFAULT createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); } } connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleCase(Node node) { // Case is a bit tricky....First it goes into the body if condition is true. createEdge(node, Branch.ON_TRUE, node.getFirstChild().getNext()); // Look for the next CASE, skipping over DEFAULT. Node next = getNextSiblingOfType(node.getNext(), Token.CASE); if (next != null) { // Found a CASE Preconditions.checkState(next.isCase()); createEdge(node, Branch.ON_FALSE, next); } else { // No more CASE found, go back and search for a DEFAULT. Node parent = node.getParent(); Node deflt = getNextSiblingOfType( parent.getFirstChild().getNext(), Token.DEFAULT_CASE); if (deflt != null) { // Has a DEFAULT createEdge(node, Branch.ON_FALSE, deflt); } else { // No DEFAULT found, go to the follow of the SWITCH. createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); } } connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleDefault(Node node) { // Directly goes to the body. It should not transfer to the next case. createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleWith(Node node) { // Directly goes to the body. It should not transfer to the next case. createEdge(node, Branch.UNCOND, node.getLastChild()); connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleStmtList(Node node) { Node parent = node.getParent(); // Special case, don't add a block of empty CATCH block to the graph. if (node.isBlock() &&

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> parent != null && parent.isTry() && NodeUtil.getCatchBlock(parent) == node && !NodeUtil.hasCatchHandler(node)) { return; } // A block transfer control to its first child if it is not empty. Node child = node.getFirstChild(); // Function declarations are skipped since control doesn't go into that // function (unless it is called) while (child != null && child.isFunction()) { child = child.getNext(); } if (child != null) { createEdge(node, Branch.UNCOND, computeFallThrough(child)); } else { createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); } // Synthetic blocks if (parent != null) { switch (parent.getType()) { case Token.DEFAULT_CASE: case Token.CASE: case Token.TRY: break; default: if (node.isBlock() && node.isSyntheticBlock()) { createEdge(node, Branch.SYN_BLOCK, computeFollowNode(node, this)); } break; } } } private void handleFunction(Node node) { // A block transfer control to its first child if it is not empty. Preconditions.checkState(node.getChildCount() >= 3); createEdge(node, Branch.UNCOND, computeFallThrough(node.getFirstChild().getNext().getNext())); Preconditions.checkState(exceptionHandler.peek() == node); exceptionHandler.pop(); } private void handleExpr(Node node) { createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); connectToPossibleExceptionHandler(node, node); } private void handleThrow(Node node) { connectToPossibleExceptionHandler(node, node); } private void handleTry(Node node) { createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleCatch(Node node) { createEdge(node, Branch.UNCOND, node.getLastChild()); } private void handleBreak(Node node) { String label = null; // See if it is a break with label. if (node.hasChildren()) { label = node.getFirstChild().getString(); }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> (cur.isTry() && NodeUtil.hasFinally(cur) && cur.getLastChild() != previous) { if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, cur.getLastChild()); } else { finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); } lastJump = cur; } Preconditions.checkState(parent != null, "Cannot find continue target."); previous = cur; } Node iter = cur; if (cur.getChildCount() == 4) { iter = cur.getFirstChild().getNext().getNext(); } if (lastJump == node) { createEdge(node, Branch.UNCOND, iter); } else { finallyMap.put(lastJump, iter); } } private void handleReturn(Node node) { Node lastJump = null; for (Iterator<Node> iter = exceptionHandler.iterator(); iter.hasNext();) { Node curHandler = iter.next(); if (curHandler.isFunction()) { break; } if (NodeUtil.hasFinally(curHandler)) { if (lastJump == null) { createEdge(node, Branch.UNCOND, curHandler.getLastChild()); } else { finallyMap.put(lastJump, computeFallThrough(curHandler.getLastChild())); } lastJump = curHandler; } } if (node.hasChildren()) { connectToPossibleExceptionHandler(node, node.getFirstChild()); } if (lastJump == null) { createEdge(node, Branch.UNCOND, null); } else { finallyMap.put(lastJump, null); } } private void handleStmt(Node node) { // Simply transfer to the next line. createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); connectToPossibleExceptionHandler(node, node); } static Node computeFollowNode(Node node, ControlFlowAnalysis cfa) { return computeFollowNode(node, node, cfa); } static Node computeFollowNode(Node node) { return computeFollowNode(node, node, null); } /** * Computes the follow() node of a given node and its parent. There is a

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> side * effect when calling this function. If this function computed an edge that * exists a FINALLY, it'll attempt to connect the fromNode to the outer * FINALLY according to the finallyMap. * * @param fromNode The original source node since {@code node} is changed * during recursion. * @param node The node that follow() should compute. */ private static Node computeFollowNode( Node fromNode, Node node, ControlFlowAnalysis cfa) { /* * This is the case where: * * 1. Parent is null implies that we are transferring control to the end of * the script. * * 2. Parent is a function implies that we are transferring control back to * the caller of the function. * * 3. If the node is a return statement, we should also transfer control * back to the caller of the function. * * 4. If the node is root then we have reached the end of what we have been * asked to traverse. * * In all cases we should transfer control to a "symbolic return" node. * This will make life easier for DFAs. */ Node parent = node.getParent(); if (parent == null || parent.isFunction() || (cfa != null && node == cfa.root)) { return null; } // If we are just before a IF/WHILE/DO/FOR: switch (parent.getType()) { // The follow() of any of the path from IF would be what follows IF. case Token.IF: return computeFollowNode(fromNode, parent, cfa); case Token.CASE: case Token.DEFAULT_CASE: // After the body of a CASE, the control goes to the body of the next // case, without having to go to the case condition. if (parent.getNext() != null) { if (parent.getNext().isCase()) { return parent.getNext().getFirstChild().getNext(); } else if (parent.getNext().isDefaultCase()) { return parent.getNext().getFirstChild(); } else { Preconditions.checkState(false, "Not reachable"); } } else { return computeFollowNode(fromNode, parent, cfa); } break

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.DO: return computeFallThrough(n.getFirstChild()); case Token.FOR: if (NodeUtil.isForIn(n)) { return n.getFirstChild().getNext(); } return computeFallThrough(n.getFirstChild()); case Token.LABEL: return computeFallThrough(n.getLastChild()); default: return n; } } /** * Connects the two nodes in the control flow graph. * * @param fromNode Source. * @param toNode Destination. */ private void createEdge(Node fromNode, ControlFlowGraph.Branch branch, Node toNode) { cfg.createNode(fromNode); cfg.createNode(toNode); cfg.connectIfNotFound(fromNode, branch, toNode); } /** * Connects cfgNode to the proper CATCH block if target subtree might throw * an exception. If there are FINALLY blocks reached before a CATCH, it will * make the corresponding entry in finallyMap. */ private void connectToPossibleExceptionHandler(Node cfgNode, Node target) { if (mayThrowException(target) && !exceptionHandler.isEmpty()) { Node lastJump = cfgNode; for (Node handler : exceptionHandler) { if (handler.isFunction()) { return; } Preconditions.checkState(handler.isTry()); Node catchBlock = NodeUtil.getCatchBlock(handler); if (!NodeUtil.hasCatchHandler(catchBlock)) { // No catch but a FINALLY. if (lastJump == cfgNode) { createEdge(cfgNode, Branch.ON_EX, handler.getLastChild()); } else { finallyMap.put(lastJump, handler.getLastChild()); } } else { // Has a catch. if (lastJump == cfgNode) { createEdge(cfgNode, Branch.ON_EX, catchBlock); return; } else { finallyMap.put(lastJump, catchBlock); } } lastJump = handler; } } } /** * Get the next sibling (including itself) of one of the given types. */ private static Node getNextSiblingOfType(Node first, int ... types) { for (Node c = first; c != null; c = c.getNext()) { for

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>> priorities, boolean edgeAnnotations) { super(entry, true /* node annotations */, edgeAnnotations); this.priorities = priorities; } @Override /** * Returns a node comparator based on the pre-order traversal of the AST. * @param isForward x 'before' y in the pre-order traversal implies * x 'less than' y (if true) and x 'greater than' y (if false). */ public Comparator<DiGraphNode<Node, Branch>> getOptionalNodeComparator( boolean isForward) { if (isForward) { return new Comparator<DiGraphNode<Node, Branch>>() { @Override public int compare( DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) { return getPosition(n1) - getPosition(n2); } }; } else { return new Comparator<DiGraphNode<Node, Branch>>() { @Override public int compare( DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) { return getPosition(n2) - getPosition(n1); } }; } } /** * Gets the pre-order traversal position of the given node. * @return An arbitrary counter used for comparing positions. */ private int getPosition(DiGraphNode<Node, Branch> n) { Integer priority = priorities.get(n); Preconditions.checkNotNull(priority); return priority; } } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> resolvedNamedTypes.values()) { type.clearResolved(); } unresolvedNamedTypes.putAll(resolvedNamedTypes); resolvedNamedTypes.clear(); } boolean isLastGeneration() { return lastGeneration; } /** * Sets whether this is the last generation. In the last generation, * {@link NamedType} warns about unresolved types. */ public void setLastGeneration(boolean lastGeneration) { this.lastGeneration = lastGeneration; } /** * Tells the type system that {@code type} implements interface {@code * interfaceInstance}. * {@code inter} must be an ObjectType for the instance of the interface as it * could be a named type and not yet have the constructor. */ void registerTypeImplementingInterface( FunctionType type, ObjectType interfaceInstance) { interfaceToImplementors.put(interfaceInstance.getReferenceName(), type); } /** * Returns a collection of types that directly implement {@code * interfaceInstance}. Subtypes of implementing types are not guaranteed to * be returned. {@code interfaceInstance} must be an ObjectType for the * instance of the interface. */ public Collection<FunctionType> getDirectImplementors( ObjectType interfaceInstance) { return interfaceToImplementors.get(interfaceInstance.getReferenceName()); } /** * Records declared global type names. This makes resolution faster * and more robust in the common case. * * @param name The name of the type to be recorded. * @param t The actual type being associated with the name. * @return True if this name is not already defined, false otherwise. */ public boolean declareType(String name, JSType t) { if (namesToTypes.containsKey(name)) { return false; } register(t, name); return true; } /** * Overrides a declared global type name. Throws an exception if this * type name hasn't been declared yet. */ public void overwriteDeclaredType(String name, JSType t) { Preconditions.checkState(namesToTypes.containsKey(name)); register(t, name); } /** * Records a forward-declared type name. We will not emit errors if this * type name never resolves to anything. */ public void forwardDeclareType(

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>true); return type; } /** * Creates a constructor function type. * @param name the function's name or {@code null} to indicate that the * function is anonymous. * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. * @param parameters the function's parameters or {@code null} * to indicate that the parameter types are unknown. * @param returnType the function's return type or {@code null} to indicate * that the return type is unknown. */ public FunctionType createConstructorType(String name, Node source, Node parameters, JSType returnType) { return new FunctionType(this, name, source, createArrowType(parameters, returnType), null, null, true, false); } /** * Creates an interface function type. * @param name the function's name * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. */ public FunctionType createInterfaceType(String name, Node source) { return FunctionType.forInterface(this, name, source); } /** * Creates a parameterized type. */ public ParameterizedType createParameterizedType( ObjectType objectType, JSType parameterType) { return new ParameterizedType(this, objectType, parameterType); } /** * Creates a named type. */ @VisibleForTesting public JSType createNamedType(String reference, String sourceName, int lineno, int charno) { return new NamedType(this, reference, sourceName, lineno, charno); } /** * Identifies the name of a typedef or enum before we actually declare it. */ public void identifyNonNullableName(String name) { Preconditions.checkNotNull(name); nonNullableTypeNames.add(name); } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope<JSType> scope) {

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Node = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) { // Duplicate field name, warning and skip reporter.warning( "Duplicate record field " + fieldName, sourceName, n.getLineno(), fieldNameNode.getCharno()); } } return builder.build(); } /** * Sets the template type name. */ public void setTemplateTypeNames(List<String> names) { Preconditions.checkNotNull(names); for (String name : names) { templateTypes.put(name, new TemplateType(this, name)); } } /** * Clears the template type name. */ public void clearTemplateTypeNames() { templateTypes.clear(); } }

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> fileOverview = null; String returnDescription = null; String version = null; List<String> authors = null; List<String> sees = null; } /** * A piece of information (found in a marker) which contains a position * with a string. */ public static class StringPosition extends SourcePosition<String> { } /** * A piece of information (found in a marker) which contains a position * with a string that has no leading or trailing whitespace. */ static class TrimmedStringPosition extends StringPosition { @Override public void setItem(String item) { Preconditions.checkArgument( item.charAt(0) != ' ' && item.charAt(item.length() - 1) != ' ', "String has leading or trailing whitespace"); super.setItem(item); } } /** * A piece of information (found in a marker) which contains a position * with a name node. */ public static class NamePosition extends SourcePosition<Node> {} /** * A piece of information (found in a marker) which contains a position * with a type expression syntax tree. */ public static class TypePosition extends SourcePosition<Node> { private boolean brackets = false; /** Returns whether the type has curly braces around it. */ public boolean hasBrackets() { return brackets; } void setHasBrackets(boolean newVal) { brackets = newVal; } } /** * Defines a class for containing the parsing information * for this JSDocInfo. For each annotation found in the * JsDoc, a marker will be created indicating the annotation * itself, the name of the annotation (if any; for example, * a @param has a name, but a @return does not), the * textual description found on that annotation and, if applicable, * the type declaration. All this information is only collected * if documentation collection is turned on. */ public static final class Marker { private TrimmedStringPosition annotation = null; private TrimmedStringPosition name = null; private SourcePosition<Node> nameNode = null; private StringPosition description = null; private TypePosition type = null; /** * Gets the position information for the annotation name. (e.g.,

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>( "JSC_ENUM_INITIALIZER_NOT_ENUM", "enum initializer must be an object literal or an enum"); static final DiagnosticType CTOR_INITIALIZER = DiagnosticType.warning( "JSC_CTOR_INITIALIZER_NOT_CTOR", "Constructor {0} must be initialized at declaration"); static final DiagnosticType IFACE_INITIALIZER = DiagnosticType.warning( "JSC_IFACE_INITIALIZER_NOT_IFACE", "Interface {0} must be initialized at declaration"); static final DiagnosticType CONSTRUCTOR_EXPECTED = DiagnosticType.warning( "JSC_REFLECT_CONSTRUCTOR_EXPECTED", "Constructor expected as first argument"); static final DiagnosticType UNKNOWN_LENDS = DiagnosticType.warning( "JSC_UNKNOWN_LENDS", "Variable {0} not declared before @lends annotation."); static final DiagnosticType LENDS_ON_NON_OBJECT = DiagnosticType.warning( "JSC_LENDS_ON_NON_OBJECT", "May only lend properties to object types. {0} has type {1}."); private final AbstractCompiler compiler; private final ErrorReporter typeParsingErrorReporter; private final TypeValidator validator; private final CodingConvention codingConvention; private final JSTypeRegistry typeRegistry; private final List<ObjectType> delegateProxyPrototypes = Lists.newArrayList(); private final Map<String, String> delegateCallingConventions = Maps.newHashMap(); // Simple properties inferred about functions. private final Map<Node, AstFunctionContents> functionAnalysisResults = Maps.newHashMap(); /** * Defer attachment of types to nodes until all type names * have been resolved. Then, we can resolve the type and attach it. */ private class DeferredSetType { final Node node; final JSType type; DeferredSetType(Node node, JSType type) { Preconditions.checkNotNull(node); Preconditions.checkNotNull(type); this.node = node; this.type = type; // Other parts of this pass may read off the node. // (like when we set the LHS of an assign with a typed RHS function.) node.setJSType(type); } void resolve(Scope scope) { node.setJSType(type

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(fnThisType)); } } } if (parent == null) { codingConvention.defineDelegateProxyPrototypeProperties( typeRegistry, newScope, delegateProxyPrototypes, delegateCallingConventions); } return newScope; } /** * Patches a given global scope by removing variables previously declared in * a script and re-traversing a new version of that script. * * @param globalScope The global scope generated by {@code createScope}. * @param scriptRoot The script that is modified. */ void patchGlobalScope(Scope globalScope, Node scriptRoot) { // Preconditions: This is supposed to be called only on (named) SCRIPT nodes // and a global typed scope should have been generated already. Preconditions.checkState(scriptRoot.isScript()); Preconditions.checkNotNull(globalScope); Preconditions.checkState(globalScope.isGlobal()); String scriptName = NodeUtil.getSourceName(scriptRoot); Preconditions.checkNotNull(scriptName); for (Node node : ImmutableList.copyOf(functionAnalysisResults.keySet())) { if (scriptName.equals(NodeUtil.getSourceName(node))) { functionAnalysisResults.remove(node); } } (new FirstOrderFunctionAnalyzer( compiler, functionAnalysisResults)).process(null, scriptRoot); // TODO(bashir): Variable declaration is not the only side effect of last // global scope generation but here we only wipe that part off! // Remove all variables that were previously declared in this scripts. // First find all vars to remove then remove them because of iterator! Iterator<Var> varIter = globalScope.getVars(); List<Var> varsToRemove = Lists.newArrayList(); while (varIter.hasNext()) { Var oldVar = varIter.next(); if (scriptName.equals(oldVar.getInputName())) { varsToRemove.add(oldVar); } } for (Var var : varsToRemove) { globalScope.undeclare(var); globalScope.getTypeOfThis().removeProperty(var.getName()); } // Now re-traverse the given script. GlobalScopeBuilder scopeBuilder = new GlobalScopeBuilder(globalScope); NodeTraversal.traverse(compiler, scriptRoot, scopeBuilder); } /** * Create the outermost

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>lends annotation resolves correctly. * * For more information, see * http://code.google.com/p/closure-compiler/issues/detail?id=314 */ private List<Node> lentObjectLiterals = null; /** * Type-less stubs. * * If at the end of traversal, we still don't have types for these * stubs, then we should declare UNKNOWN types. */ private final List<StubDeclaration> stubDeclarations = Lists.newArrayList(); /** * The current source file that we're in. */ private String sourceName = null; /** * The InputId of the current node. */ private InputId inputId; private AbstractScopeBuilder(Scope scope) { this.scope = scope; } void setDeferredType(Node node, JSType type) { deferredSetTypes.add(new DeferredSetType(node, type)); } void resolveTypes() { // Resolve types and attach them to nodes. for (DeferredSetType deferred : deferredSetTypes) { deferred.resolve(scope); } // Resolve types and attach them to scope slots. Iterator<Var> vars = scope.getVars(); while (vars.hasNext()) { vars.next().resolveType(typeParsingErrorReporter); } // Tell the type registry that any remaining types // are unknown. typeRegistry.resolveTypesInScope(scope); } @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); if (n.isFunction() || n.isScript()) { Preconditions.checkNotNull(inputId); sourceName = NodeUtil.getSourceName(n); } // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. boolean descend = parent == null || !parent.isFunction() || n == parent.getFirstChild() || parent == scope.getRootNode(); if (descend) { // Handle hoisted functions on pre-order traversal, so that they // get hit before other things in the scope. if (NodeUtil.isStatementParent(n)) { for (Node child = n.getFirstChild(); child

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(keyNode, keyType); } if (keyType != null && objLitType != null && declareOnOwner) { // Declare this property on its object literal. boolean isExtern = keyNode.isFromExterns(); objLitType.defineDeclaredProperty(memberName, keyType, keyNode); } } } /** * Returns the type specified in a JSDoc annotation near a GETPROP or NAME. * * Extracts type information from either the {@code @type} tag or from * the {@code @return} and {@code @param} tags. */ private JSType getDeclaredTypeInAnnotation(String sourceName, Node node, JSDocInfo info) { JSType jsType = null; Node objNode = node.isGetProp() ? node.getFirstChild() : NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() : null; if (info != null) { if (info.hasType()) { jsType = info.getType().evaluate(scope, typeRegistry); } else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) { String fnName = node.getQualifiedName(); jsType = createFunctionTypeFromNodes( null, fnName, info, node); } } return jsType; } /** * Asserts that it's OK to define this node's name. * The node should have a source name and be of the specified type. */ void assertDefinitionNode(Node n, int type) { Preconditions.checkState(sourceName != null); Preconditions.checkState(n.getType() == type); } /** * Defines a catch parameter. */ void defineCatch(Node n, Node parent) { assertDefinitionNode(n, Token.CATCH); Node catchName = n.getFirstChild(); defineSlot(catchName, n, null); } /** * Defines a VAR initialization. */ void defineVar(Node n, Node parent) { assertDefinitionNode(n, Token.VAR); JSDocInfo info = n.getJSDocInfo(); if (n.hasMoreThanOneChild()) { if (info != null) { // multiple children compiler.report(JSError.make

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, but * are not mutually exclusive: * - An object literal that needs an enum type attached to it. * - An assignment expression with an enum tag in the JsDoc. * * This function will always create an enum type, so only call it if * you're sure that's what you want. * * @param rValue The node of the enum. * @param name The enum's name * @param info The {@link JSDocInfo} attached to the enum definition. * @param lValueNode The node where this function is being * assigned. */ private EnumType createEnumTypeFromNodes(Node rValue, String name, JSDocInfo info, Node lValueNode) { Preconditions.checkNotNull(info); Preconditions.checkState(info.hasEnumParameterType()); EnumType enumType = null; if (rValue != null && rValue.isQualifiedName()) { // Handle an aliased enum. Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() instanceof EnumType) { enumType = (EnumType) var.getType(); } } if (enumType == null) { JSType elementsType = info.getEnumParameterType().evaluate(scope, typeRegistry); enumType = typeRegistry.createEnumType(name, rValue, elementsType); if (rValue != null && rValue.isObjectLit()) { // collect enum elements Node key = rValue.getFirstChild(); while (key != null) { String keyName = NodeUtil.getStringValue(key); if (keyName == null) { // GET and SET don't have a String value; compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else if (!codingConvention.isValidEnumKey(keyName)) { compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else { enumType.defineElement(keyName, key); } key = key.getNext(); } } } if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, enumType.getElementsType()); } return

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> enumType; } /** * Defines a typed variable. The defining node will be annotated with the * variable's type or {@code null} if its type is inferred. * @param name the defining node. It must be a {@link Token#NAME}. * @param parent the {@code name}'s parent. * @param type the variable's type. It may be {@code null}, in which case * the variable's type will be inferred. */ private void defineSlot(Node name, Node parent, JSType type) { defineSlot(name, parent, type, type == null); } /** * Defines a typed variable. The defining node will be annotated with the * variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is * inferred. * * Slots may be any variable or any qualified name in the global scope. * * @param n the defining NAME or GETPROP node. * @param parent the {@code n}'s parent. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. */ void defineSlot(Node n, Node parent, JSType type, boolean inferred) { Preconditions.checkArgument(inferred || type != null); // Only allow declarations of NAMEs and qualified names. // Object literal keys will have to compute their names themselves. if (n.isName()) { Preconditions.checkArgument( parent.isFunction() || parent.isVar() || parent.isParamList() || parent.isCatch()); } else { Preconditions.checkArgument( n.isGetProp() && (parent.isAssign() || parent.isExprResult())); } defineSlot(n, parent, n.getQualifiedName(), type, inferred); } /** * Defines a symbol in the current scope. * * @param n the defining NAME or GETPROP or object literal key node. * @param parent the {@code n}'s parent. * @param variableName The name that this should be known by. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. * @param inferred Whether the type is inferred or

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> declared. */ void defineSlot(Node n, Node parent, String variableName, JSType type, boolean inferred) { Preconditions.checkArgument(!variableName.isEmpty()); boolean isGlobalVar = n.isName() && scope.isGlobal(); boolean shouldDeclareOnGlobalThis = isGlobalVar && (parent.isVar() || parent.isFunction()); // If n is a property, then we should really declare it in the // scope where the root object appears. This helps out people // who declare "global" names in an anonymous namespace. Scope scopeToDeclareIn = scope; if (n.isGetProp() && !scope.isGlobal() && isQnameRootedInGlobalScope(n)) { Scope globalScope = scope.getGlobalScope(); // don't try to declare in the global scope if there's // already a symbol there with this name. if (!globalScope.isDeclared(variableName, false)) { scopeToDeclareIn = scope.getGlobalScope(); } } // The input may be null if we are working with a AST snippet. So read // the extern info from the node. boolean isExtern = n.isFromExterns(); Var newVar = null; // declared in closest scope? CompilerInput input = compiler.getInput(inputId); if (scopeToDeclareIn.isDeclared(variableName, false)) { Var oldVar = scopeToDeclareIn.getVar(variableName); newVar = validator.expectUndeclaredVariable( sourceName, input, n, parent, oldVar, variableName, type); } else { if (!inferred) { setDeferredType(n, type); } newVar = scopeToDeclareIn.declare(variableName, n, type, input, inferred); if (type instanceof EnumType) { Node initialValue = newVar.getInitialValue(); boolean isValidValue = initialValue != null && (initialValue.isObjectLit() || initialValue.isQualifiedName()); if (!isValidValue) { compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER)); } } } // We need to do some additional work for constructors and interfaces. FunctionType fnType = JSType.toMaybeFunctionType(type

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>ClassName)); if (objectType != null) { FunctionType functionType = objectType.getConstructor(); if (functionType != null) { FunctionType getterType = typeRegistry.createFunctionType(objectType); codingConvention.applySingletonGetter(functionType, getterType, objectType); } } } DelegateRelationship delegateRelationship = codingConvention.getDelegateRelationship(n); if (delegateRelationship != null) { applyDelegateRelationship(delegateRelationship); } ObjectLiteralCast objectLiteralCast = codingConvention.getObjectLiteralCast(n); if (objectLiteralCast != null) { if (objectLiteralCast.diagnosticType == null) { ObjectType type = ObjectType.cast( typeRegistry.getType(objectLiteralCast.typeName)); if (type != null && type.getConstructor() != null) { setDeferredType(objectLiteralCast.objectNode, type); } else { compiler.report(JSError.make(t.getSourceName(), n, CONSTRUCTOR_EXPECTED)); } } else { compiler.report(JSError.make(t.getSourceName(), n, objectLiteralCast.diagnosticType)); } } } /** * Apply special properties that only apply to delegates. */ private void applyDelegateRelationship( DelegateRelationship delegateRelationship) { ObjectType delegatorObject = ObjectType.cast( typeRegistry.getType(delegateRelationship.delegator)); ObjectType delegateBaseObject = ObjectType.cast( typeRegistry.getType(delegateRelationship.delegateBase)); ObjectType delegateSuperObject = ObjectType.cast( typeRegistry.getType(codingConvention.getDelegateSuperclassName())); if (delegatorObject != null && delegateBaseObject != null && delegateSuperObject != null) { FunctionType delegatorCtor = delegatorObject.getConstructor(); FunctionType delegateBaseCtor = delegateBaseObject.getConstructor(); FunctionType delegateSuperCtor = delegateSuperObject.getConstructor(); if (delegatorCtor != null && delegateBaseCtor != null && delegateSuperCtor != null) { FunctionParamBuilder functionParamBuilder = new FunctionParamBuilder(typeRegistry); functionParamBuilder.addRequiredParams( getNativeType(U2U_CONSTRUCTOR_TYPE)); FunctionType findDelegate = typeRegistry.createFunctionType( typeRegistry.createDefaultObjectUnion

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(delegateBaseObject), functionParamBuilder.build()); FunctionType delegateProxy = typeRegistry.createConstructorType( delegateBaseObject.getReferenceName() + DELEGATE_PROXY_SUFFIX, null, null, null); delegateProxy.setPrototypeBasedOn(delegateBaseObject); codingConvention.applyDelegateRelationship( delegateSuperObject, delegateBaseObject, delegatorObject, delegateProxy, findDelegate); delegateProxyPrototypes.add(delegateProxy.getPrototype()); } } } /** * Declare the symbol for a qualified name in the global scope. * * @param info The doc info for this property. * @param n A top-level GETPROP node (it should not be contained inside * another GETPROP). * @param parent The parent of {@code n}. * @param rhsValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ void maybeDeclareQualifiedName(NodeTraversal t, JSDocInfo info, Node n, Node parent, Node rhsValue) { Node ownerNode = n.getFirstChild(); String ownerName = ownerNode.getQualifiedName(); String qName = n.getQualifiedName(); String propName = n.getLastChild().getString(); Preconditions.checkArgument(qName != null && ownerName != null); // Precedence of type information on GETPROPs: // 1) @type annotation / @enum annotation // 2) ASSIGN to FUNCTION literal // 3) @param/@return annotation (with no function literal) // 4) ASSIGN to something marked @const // 5) ASSIGN to anything else // // 1, 3, and 4 are declarations, 5 is inferred, and 2 is a declaration iff // the function has JsDoc or has not been declared before. // // FUNCTION literals are special because TypedScopeCreator is very smart // about getting as much type information as possible for them. // Determining type for #1 + #2 + #3 + #4 JSType valueType = getDeclaredType(t.getSourceName(), info, n, rhsValue); if (valueType == null && rhsValue != null) { // Determining type for #5 valueType = rhsValue.

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> null || !info.hasTypedefType()) { return; } String typedef = candidate.getQualifiedName(); if (typedef == null) { return; } // TODO(nicksantos|user): This is a terrible, terrible hack // to bail out on recursive typedefs. We'll eventually need // to handle these properly. typeRegistry.declareType(typedef, getNativeType(UNKNOWN_TYPE)); JSType realType = info.getTypedefType().evaluate(scope, typeRegistry); if (realType == null) { compiler.report( JSError.make( t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); } typeRegistry.overwriteDeclaredType(typedef, realType); if (candidate.isGetProp()) { defineSlot(candidate, candidate.getParent(), getNativeType(NO_TYPE), false); } } } // end GlobalScopeBuilder /** * A shallow traversal of a local scope to find all arguments and * local variables. */ private final class LocalScopeBuilder extends AbstractScopeBuilder { /** * @param scope The scope that we're building. */ private LocalScopeBuilder(Scope scope) { super(scope); } /** * Traverse the scope root and build it. */ void build() { NodeTraversal.traverse(compiler, scope.getRootNode(), this); AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents != null) { for (String varName : contents.getEscapedVarNames()) { Var v = scope.getVar(varName); Preconditions.checkState(v.getScope() == scope); v.markEscaped(); } } } /** * Visit a node in a local scope, and add any local variables or catch * parameters into the local symbol table. * * @param t The node traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n == scope.getRootNode()) return; if (n.isParamList() && parent == scope.getRootNode()) { handleFunctionInputs(parent); return;

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Creator(compiler); typedScopeCreator = new MemoizedScopeCreator(internalScopeCreator); topScope = typedScopeCreator.createScope(root, null); } /** * Regenerates the top scope potentially only for a sub-tree of AST and then * copies information for the old global scope. * * @param compiler The compiler for which the global scope is generated. * @param scriptRoot The root of the AST used to generate global scope. */ void patchGlobalTypedScope(AbstractCompiler compiler, Node scriptRoot) { Preconditions.checkNotNull(internalScopeCreator); internalScopeCreator.patchGlobalScope(topScope, scriptRoot); } /** * Gets the scope creator for typed scopes. */ MemoizedScopeCreator getTypedScopeCreator() { return typedScopeCreator; } /** * Gets the global scope, with type information. */ Scope getTopScope() { return topScope; } /** * Gets the checking passes to run. * * Checking passes revolve around emitting warnings and errors. * They also may include pre-processor passes needed to do * error analysis more effectively. * * Clients that only want to analyze code (like IDEs) and not emit * code will only run checks and not optimizations. */ abstract protected List<PassFactory> getChecks(); /** * Gets the optimization passes to run. * * Optimization passes revolve around producing smaller and faster code. * They should always run after checking passes. */ abstract protected List<PassFactory> getOptimizations(); /** * Gets a graph of the passes run. For debugging. */ GraphvizGraph getPassGraph() { LinkedDirectedGraph<String, String> graph = LinkedDirectedGraph.createWithoutAnnotations(); Iterable<PassFactory> allPasses = Iterables.concat(getChecks(), getOptimizations()); String lastPass = null; String loopStart = null; for (PassFactory pass : allPasses) { String passName = pass.getName(); int i = 1; while (graph.hasNode(passName)) { passName = pass.getName() + (i++); } graph.createNode(passName); if (loopStart == null && !pass.isOneTime

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>reportCodeChange(); } else if ("setCssNameMapping".equals(methodName)) { processSetCssNameMapping(t, n, parent); } } } break; case Token.ASSIGN: case Token.NAME: // If this is an assignment to a provided name, remove the provided // object. handleCandidateProvideDefinition(t, n, parent); break; case Token.EXPR_RESULT: handleTypedefDefinition(t, n, parent); break; case Token.FUNCTION: // If this is a declaration of a provided named function, this is an // error. Hoisted functions will explode if they're provided. if (t.inGlobalScope() && !NodeUtil.isFunctionExpression(n)) { String name = n.getFirstChild().getString(); ProvidedName pn = providedNames.get(name); if (pn != null) { compiler.report(t.makeError(n, FUNCTION_NAMESPACE_ERROR, name)); } } break; case Token.NEW: trySimplifyNewDate(t, n, parent); break; case Token.GETPROP: if (n.getFirstChild().isName() && !parent.isCall() && !parent.isAssign() && "goog.base".equals(n.getQualifiedName())) { reportBadBaseClassUse(t, n, "May only be called directly."); } break; } } /** * Handles a goog.require call. */ private void processRequireCall(NodeTraversal t, Node n, Node parent) { Node left = n.getFirstChild(); Node arg = left.getNext(); if (verifyArgument(t, left, arg)) { String ns = arg.getString(); ProvidedName provided = providedNames.get(ns); if (provided == null || !provided.isExplicitlyProvided()) { unrecognizedRequires.add( new UnrecognizedRequire(n, ns, t.getSourceName())); } else { JSModule providedModule = provided.explicitModule; // This must be non-null, because there was an explicit provide. Preconditions.checkNotNull(providedModule); JSModule module = t.getModule(); if (moduleGraph != null && module != providedModule && !module

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Map(cssRenamingMap); parent.getParent().removeChild(parent); compiler.reportCodeChange(); } } /** * Try to simplify "new Date(goog.now())" to "new Date()". */ private void trySimplifyNewDate(NodeTraversal t, Node n, Node parent) { if (!rewriteNewDateGoogNow) { return; } Preconditions.checkArgument(n.isNew()); Node date = n.getFirstChild(); if (!date.isName() || !"Date".equals(date.getString())) { return; } Node callGoogNow = date.getNext(); if (callGoogNow == null || !callGoogNow.isCall() || callGoogNow.getNext() != null) { return; } Node googNow = callGoogNow.getFirstChild(); String googNowQName = googNow.getQualifiedName(); if (googNowQName == null || !"goog.now".equals(googNowQName) || googNow.getNext() != null) { return; } n.removeChild(callGoogNow); compiler.reportCodeChange(); } /** * Verifies that a provide method call has exactly one argument, * and that it's a string literal and that the contents of the string are * valid JS tokens. Reports a compile error if it doesn't. * * @return Whether the argument checked out okay */ private boolean verifyProvide(NodeTraversal t, Node methodName, Node arg) { if (!verifyArgument(t, methodName, arg)) { return false; } for (String part : arg.getString().split("\\.")) { if (!NodeUtil.isValidPropertyName(part)) { compiler.report(t.makeError(arg, INVALID_PROVIDE_ERROR, part)); return false; } } return true; } /** * Verifies that a method call has exactly one argument, and that it's a * string literal. Reports a compile error if it doesn't. * * @return Whether the argument checked out okay */ private boolean verifyArgument(NodeTraversal t, Node methodName, Node arg) { return verifyArgument(t, methodName, arg, Token.STRING); } /** * Verifies that a method call

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> String prefixNs = ns.substring(0, pos); pos = ns.indexOf('.', pos + 1); if (providedNames.containsKey(prefixNs)) { providedNames.get(prefixNs).addProvide( node, module, false /* implicit */); } else { providedNames.put( prefixNs, new ProvidedName(prefixNs, node, module, false /* implicit */)); } } } // ------------------------------------------------------------------------- /** * Information required to replace a goog.provide call later in the traversal. */ private class ProvidedName { private final String namespace; // The node and module where the call was explicitly or implicitly // goog.provided. private final Node firstNode; private final JSModule firstModule; // The node where the call was explicitly goog.provided. May be null // if the namespace is always provided implicitly. private Node explicitNode = null; private JSModule explicitModule = null; // The candidate definition. private Node candidateDefinition = null; // The minimum module where the provide must appear. private JSModule minimumModule = null; // The replacement declaration. private Node replacementNode = null; ProvidedName(String namespace, Node node, JSModule module, boolean explicit) { Preconditions.checkArgument( node == null /* The base case */ || node.isExprResult()); this.namespace = namespace; this.firstNode = node; this.firstModule = module; addProvide(node, module, explicit); } /** * Add an implicit or explicit provide. */ void addProvide(Node node, JSModule module, boolean explicit) { if (explicit) { Preconditions.checkState(explicitNode == null); Preconditions.checkArgument(node.isExprResult()); explicitNode = node; explicitModule = module; } updateMinimumModule(module); } boolean isExplicitlyProvided() { return explicitNode != null; } /** * Record function declaration, variable declaration or assignment that * refers to the same name as the provide statement. Give preference to * declarations; if no declaration exists, record a reference to an * assignment so it repurposed later. */ void addDefinition(Node node, JSModule module) { Preconditions.checkArgument(node

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.isExprResult() || // assign node.isFunction() || node.isVar()); Preconditions.checkArgument(explicitNode != node); if ((candidateDefinition == null) || !node.isExprResult()) { candidateDefinition = node; updateMinimumModule(module); } } private void updateMinimumModule(JSModule newModule) { if (minimumModule == null) { minimumModule = newModule; } else if (moduleGraph != null) { minimumModule = moduleGraph.getDeepestCommonDependencyInclusive( minimumModule, newModule); } else { // If there is no module graph, then there must be exactly one // module in the program. Preconditions.checkState(newModule == minimumModule, "Missing module graph"); } } /** * Replace the provide statement. * * If we're providing a name with no definition, then create one. * If we're providing a name with a duplicate definition, then make sure * that definition becomes a declaration. */ void replace() { if (firstNode == null) { // Don't touch the base case ('goog'). replacementNode = candidateDefinition; return; } // Handle the case where there is a duplicate definition for an explicitly // provided symbol. if (candidateDefinition != null && explicitNode != null) { explicitNode.detachFromParent(); compiler.reportCodeChange(); // Does this need a VAR keyword? replacementNode = candidateDefinition; if (candidateDefinition.isExprResult() && !candidateDefinition.getFirstChild().isQualifiedName()) { candidateDefinition.putBooleanProp(Node.IS_NAMESPACE, true); Node assignNode = candidateDefinition.getFirstChild(); Node nameNode = assignNode.getFirstChild(); if (nameNode.isName()) { // Need to convert this assign to a var declaration. Node valueNode = nameNode.getNext(); assignNode.removeChild(nameNode); assignNode.removeChild(valueNode); nameNode.addChildToFront(valueNode); Node varNode = IR.var(nameNode); varNode.copyInformationFrom(candidateDefinition); candidateDefinition.getParent().replaceChild( candidateDefinition, varNode); nameNode.setJSDocInfo(assignNode.getJSDocInfo());

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> compiler.reportCodeChange(); replacementNode = varNode; } } } else { // Handle the case where there's not a duplicate definition. replacementNode = createDeclarationNode(); if (firstModule == minimumModule) { firstNode.getParent().addChildBefore(replacementNode, firstNode); } else { // In this case, the name was implicitly provided by two independent // modules. We need to move this code up to a common module. int indexOfDot = namespace.lastIndexOf('.'); if (indexOfDot == -1) { // Any old place is fine. compiler.getNodeForCodeInsertion(minimumModule) .addChildToBack(replacementNode); } else { // Add it after the parent namespace. ProvidedName parentName = providedNames.get(namespace.substring(0, indexOfDot)); Preconditions.checkNotNull(parentName); Preconditions.checkNotNull(parentName.replacementNode); parentName.replacementNode.getParent().addChildAfter( replacementNode, parentName.replacementNode); } } if (explicitNode != null) { explicitNode.detachFromParent(); } compiler.reportCodeChange(); } } /** * Create the declaration node for this name, without inserting it * into the AST. */ private Node createDeclarationNode() { if (namespace.indexOf('.') == -1) { return makeVarDeclNode(); } else { return makeAssignmentExprNode(); } } /** * Creates a simple namespace variable declaration * (e.g. <code>var foo = {};</code>). */ private Node makeVarDeclNode() { Node name = IR.name(namespace); name.addChildToFront(createNamespaceLiteral()); Node decl = IR.var(name); decl.putBooleanProp(Node.IS_NAMESPACE, true); // TODO(nicksantos): ew ew ew. Create a mutator package. if (compiler.getCodingConvention().isConstant(namespace)) { name.putBooleanProp(Node.IS_CONSTANT_NAME, true); } if (candidateDefinition == null) { name.setJSDocInfo(createConstantJsDoc()); } Preconditions.checkState(isNamespacePlaceholder(decl)); setSourceInfo(decl); return decl

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>; } /** * There are some special cases where clients of the compiler * do not run TypedScopeCreator after running this pass. * So always give the namespace literal a type. */ private Node createNamespaceLiteral() { Node objlit = IR.objectlit(); objlit.setJSType( compiler.getTypeRegistry().createAnonymousObjectType()); return objlit; } /** * Creates a dotted namespace assignment expression * (e.g. <code>foo.bar = {};</code>). */ private Node makeAssignmentExprNode() { Node decl = IR.exprResult( IR.assign( NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), namespace, firstNode /* real source info will be filled in below */, namespace), createNamespaceLiteral())); decl.putBooleanProp(Node.IS_NAMESPACE, true); if (candidateDefinition == null) { decl.getFirstChild().setJSDocInfo(createConstantJsDoc()); } Preconditions.checkState(isNamespacePlaceholder(decl)); setSourceInfo(decl); return decl; } private JSDocInfo createConstantJsDoc() { JSDocInfoBuilder builder = new JSDocInfoBuilder(false); builder.recordConstancy(); return builder.build(null); } /** * Copy source info to the new node. */ private void setSourceInfo(Node newNode) { Node provideStringNode = getProvideStringNode(); int offset = getSourceInfoOffset(provideStringNode); Node sourceInfoNode = provideStringNode == null ? firstNode : provideStringNode; newNode.copyInformationFromForTree(sourceInfoNode); if (offset != 0) { newNode.setSourceEncodedPositionForTree( sourceInfoNode.getSourcePosition() + offset); } } /** * Get the offset into the provide node where the symbol appears. */ private int getSourceInfoOffset(Node provideStringNode) { if (provideStringNode == null) { return 0; } int indexOfLastDot = namespace.lastIndexOf('.'); // +1 for the opening quote // +1 for the dot // if there's no dot, then the -1 index cancels it out // so elegant! return 2

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> this function. It is only relevant for * constructors and may be {@code null}. */ private List<FunctionType> subTypes; /** * The template type name. May be {@code null}. */ private final ImmutableList<String> templateTypeNames; /** Creates an instance for a function that might be a constructor. */ FunctionType(JSTypeRegistry registry, String name, Node source, ArrowType arrowType, ObjectType typeOfThis, ImmutableList<String> templateTypeNames, boolean isConstructor, boolean nativeType) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE), nativeType); setPrettyPrint(true); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkNotNull(arrowType); this.source = source; this.kind = isConstructor ? Kind.CONSTRUCTOR : Kind.ORDINARY; if (isConstructor) { this.typeOfThis = typeOfThis != null ? typeOfThis : new InstanceObjectType(registry, this, nativeType); } else { this.typeOfThis = typeOfThis != null ? typeOfThis : registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); } this.call = arrowType; this.templateTypeNames = templateTypeNames != null ? templateTypeNames : ImmutableList.<String>of(); } /** Creates an instance for a function that is an interface. */ private FunctionType(JSTypeRegistry registry, String name, Node source) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE)); setPrettyPrint(true); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkArgument(name != null); this.source = source; this.call = new ArrowType(registry, new Node(Token.PARAM_LIST), null); this.kind = Kind.INTERFACE; this.typeOfThis = new InstanceObjectType(registry, this); this.templateTypeNames = ImmutableList.of(); } /** Creates an instance for a function that is an interface. */ static FunctionType forInterface( JSTypeRegistry registry, String name, Node source) { return new FunctionType(registry, name

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> } return builder.build(); } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { if ("prototype".equals(name)) { ObjectType objType = type.toObjectType(); if (objType != null) { if (prototypeSlot != null && objType.isEquivalentTo(prototypeSlot.getType())) { return true; } this.setPrototypeBasedOn(objType, propertyNode); return true; } else { return false; } } return super.defineProperty(name, type, inferred, propertyNode); } /** * Computes the supremum or infimum of two functions. * Because sup() and inf() share a lot of logic for functions, we use * a single helper. * @param leastSuper If true, compute the supremum of {@code this} with * {@code that}. Otherwise, compute the infimum. * @return The least supertype or greatest subtype. */ FunctionType supAndInfHelper(FunctionType that, boolean leastSuper) { // NOTE(nicksantos): When we remove the unknown type, the function types // form a lattice with the universal constructor at the top of the lattice, // and the LEAST_FUNCTION_TYPE type at the bottom of the lattice. // // When we introduce the unknown type, it's much more difficult to make // heads or tails of the partial ordering of types, because there's no // clear hierarchy between the different components (parameter types and // return types) in the ArrowType. // // Rather than make the situation more complicated by introducing new // types (like unions of functions), we just fallback on the simpler // approach of getting things right at the top and the bottom of the // lattice. // // If there are unknown parameters or return types making things // ambiguous, then sup(A, B) is always the top function type, and // inf(A, B) is always the bottom function type. Preconditions.checkNotNull(that); if (isEquivalentTo(that)) { return this; } // If these are ordinary functions, then merge them. // Don't do this if any of the params/return // values are unknown, because then there will be

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>ParamsNode = call.parameters; } else { // If the parameters are not equal, don't try to merge them. // Someday, we should try to merge the individual params. return null; } JSType newReturnType = leastSuper ? call.returnType.getLeastSupertype(other.call.returnType) : call.returnType.getGreatestSubtype(other.call.returnType); ObjectType newTypeOfThis = null; if (isEquivalent(typeOfThis, other.typeOfThis)) { newTypeOfThis = typeOfThis; } else { JSType maybeNewTypeOfThis = leastSuper ? typeOfThis.getLeastSupertype(other.typeOfThis) : typeOfThis.getGreatestSubtype(other.typeOfThis); if (maybeNewTypeOfThis instanceof ObjectType) { newTypeOfThis = (ObjectType) maybeNewTypeOfThis; } else { newTypeOfThis = leastSuper ? registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE) : registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE); } } boolean newReturnTypeInferred = call.returnTypeInferred || other.call.returnTypeInferred; return new FunctionType( registry, null, null, new ArrowType( registry, newParamsNode, newReturnType, newReturnTypeInferred), newTypeOfThis, null, false, false); } /** * Given a constructor or an interface type, get its superclass constructor * or {@code null} if none exists. */ public FunctionType getSuperClassConstructor() { Preconditions.checkArgument(isConstructor() || isInterface()); ObjectType maybeSuperInstanceType = getPrototype().getImplicitPrototype(); if (maybeSuperInstanceType == null) { return null; } return maybeSuperInstanceType.getConstructor(); } /** * Given an interface and a property, finds the top-most super interface * that has the property defined (including this interface). */ public static ObjectType getTopDefiningInterface(ObjectType type, String propertyName) { ObjectType foundType = null; if (type.hasProperty(propertyName)) { foundType = type; } for (ObjectType interfaceType : type.getCtorExtendedInterfaces()) { if (interfaceType.hasProperty(propertyName)) { found

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Type = getTopDefiningInterface(interfaceType, propertyName); } } return foundType; } /** * Given a constructor or an interface type and a property, finds the * top-most superclass that has the property defined (including this * constructor). */ public ObjectType getTopMostDefiningType(String propertyName) { Preconditions.checkState(isConstructor() || isInterface()); Preconditions.checkArgument(getInstanceType().hasProperty(propertyName)); FunctionType ctor = this; if (isInterface()) { return getTopDefiningInterface(this.getInstanceType(), propertyName); } ObjectType topInstanceType = null; do { topInstanceType = ctor.getInstanceType(); ctor = ctor.getSuperClassConstructor(); } while (ctor != null && ctor.getPrototype().hasProperty(propertyName)); return topInstanceType; } /** * Two function types are equal if their signatures match. Since they don't * have signatures, two interfaces are equal if their names match. */ @Override public boolean isEquivalentTo(JSType otherType) { FunctionType that = JSType.toMaybeFunctionType(otherType); if (that == null) { return false; } if (this.isConstructor()) { if (that.isConstructor()) { return this == that; } return false; } if (this.isInterface()) { if (that.isInterface()) { return this.getReferenceName().equals(that.getReferenceName()); } return false; } if (that.isInterface()) { return false; } return this.typeOfThis.isEquivalentTo(that.typeOfThis) && this.call.isEquivalentTo(that.call); } @Override public int hashCode() { return isInterface() ? getReferenceName().hashCode() : call.hashCode(); } public boolean hasEqualCallType(FunctionType otherType) { return this.call.isEquivalentTo(otherType.call); } /** * Informally, a function is represented by * {@code function (params): returnType} where the {@code params} is a comma * separated list of types, the first one being a special * {@code this:T} if the function expects a known type

Closure, 16

<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB> private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; <CHANGES> <CHANGEE> private final String aliasName; <CHANGES> AliasedTypeNode(Node typeReference, <CHANGEE> String aliasName) { this.typeReference = typeReference; <CHANGES> <CHANGEE> this.aliasName = aliasName; } @Override public void applyAlias() { <CHANGES> typeReference.setString(aliasName); <CHANGEE> } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList(); private final List<Node> scopeCalls = Lists.newArrayList(); private final List<AliasUsage> aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map<String, Var> aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); <CHANGES> aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>variant of the other, // then we'll treat them as covariant (see comment above). other.typeOfThis.isSubtype(this.typeOfThis) || this.typeOfThis.isSubtype(other.typeOfThis); return treatThisTypesAsCovariant && this.call.isSubtype(other.call); } return getNativeType(JSTypeNative.FUNCTION_PROTOTYPE).isSubtype(that); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseFunctionType(this); } /** * Gets the type of instance of this function. * @throws IllegalStateException if this function is not a constructor * (see {@link #isConstructor()}). */ public ObjectType getInstanceType() { Preconditions.checkState(hasInstanceType()); return typeOfThis; } /** * Sets the instance type. This should only be used for special * native types. */ void setInstanceType(ObjectType instanceType) { typeOfThis = instanceType; } /** * Returns whether this function type has an instance type. */ public boolean hasInstanceType() { return isConstructor() || isInterface(); } /** * Gets the type of {@code this} in this function. */ @Override public ObjectType getTypeOfThis() { return typeOfThis.isNoObjectType() ? registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE) : typeOfThis; } /** * Gets the source node or null if this is an unknown function. */ public Node getSource() { return source; } /** * Sets the source node. */ public void setSource(Node source) { if (prototypeSlot != null) { // NOTE(bashir): On one hand when source is null we want to drop any // references to old nodes retained in prototypeSlot. On the other hand // we cannot simply drop prototypeSlot, so we retain all information // except the propertyNode for which we use an approximation! These // details mostly matter in hot-swap passes. if (source == null || prototypeSlot.getNode() == null) { prototypeSlot = new Property(prototypeSlot.getName(), prototypeSlot.getType(), prototypeSlot.isTypeInferred(), source); }